FEATURE: Allow admins to delete user SSO records in the UI (#10669)

Also displays the user's last payload in the admin UI to help with debugging SSO issues.
This commit is contained in:
Penar Musaraj 2020-09-15 10:00:10 -04:00 committed by GitHub
parent 6d7b8a71c0
commit 273db57d6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 80 additions and 1 deletions

View File

@ -132,6 +132,11 @@ export default Controller.extend(CanCheckEmails, {
.catch(() => bootbox.alert(I18n.t("generic_error")));
},
@discourseComputed("model.single_sign_on_record.last_payload")
ssoPayload(lastPayload) {
return lastPayload.split("&");
},
actions: {
impersonate() {
return this.model.impersonate();
@ -321,5 +326,16 @@ export default Controller.extend(CanCheckEmails, {
resetPrimaryGroup() {
this.set("model.primary_group_id", this.originalPrimaryGroupId);
},
deleteSSORecord() {
return bootbox.confirm(
I18n.t("admin.user.sso.confirm_delete"),
I18n.t("no_value"),
I18n.t("yes_value"),
() => {
return this.model.deleteSSORecord();
}
);
},
},
});

View File

@ -567,6 +567,16 @@ const AdminUser = User.extend({
_formatError(event) {
return `http: ${event.status} - ${event.body}`;
},
deleteSSORecord() {
return ajax(`/admin/users/${this.id}/sso_record.json`, {
type: "DELETE",
})
.then(() => {
this.set("single_sign_on_record", null);
})
.catch(popupAjaxError);
},
});
AdminUser.reopenClass({

View File

@ -652,6 +652,16 @@
<div class="display-row">
<div class="field">{{i18n "admin.user.sso.external_id"}}</div>
<div class="value">{{sso.external_id}}</div>
{{#if model.can_delete_sso_record}}
<div class="controls">
{{d-button
class="btn-danger"
action=(action "deleteSSORecord")
icon="far-trash-alt"
label="admin.user.sso.delete_sso_record"
}}
</div>
{{/if}}
</div>
<div class="display-row">
<div class="field">{{i18n "admin.user.sso.external_username"}}</div>
@ -671,6 +681,16 @@
<div class="field">{{i18n "admin.user.sso.external_avatar_url"}}</div>
<div class="value">{{sso.external_avatar_url}}</div>
</div>
{{#if sso.last_payload}}
<div class="display-row">
<div class="field">{{i18n "admin.user.sso.last_payload"}}</div>
<div class="value">
{{#each ssoPayload as |line|}}
{{line}}<br>
{{/each}}
</div>
</div>
{{/if}}
{{/with}}
</section>
{{/if}}

View File

@ -49,6 +49,7 @@
max-width: 350px;
min-width: 50px;
margin-left: 12px;
word-break: break-word;
.select-kit {
min-width: 100px;
}

View File

@ -23,7 +23,8 @@ class Admin::UsersController < Admin::AdminController
:merge,
:reset_bounce_score,
:disable_second_factor,
:delete_posts_batch]
:delete_posts_batch,
:sso_record]
def index
users = ::AdminUserIndexQuery.new(params).find_users
@ -498,6 +499,12 @@ class Admin::UsersController < Admin::AdminController
render json: success_json
end
def sso_record
guardian.ensure_can_delete_sso_record!(@user)
@user.single_sign_on_record.destroy!
render json: success_json
end
private
def perform_post_action

View File

@ -31,6 +31,7 @@ class AdminDetailedUserSerializer < AdminUserSerializer
:can_view_action_logs,
:second_factor_enabled,
:can_disable_second_factor,
:can_delete_sso_record,
:api_key_count
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
@ -126,4 +127,8 @@ class AdminDetailedUserSerializer < AdminUserSerializer
def api_key_count
object.api_keys.active.count
end
def can_delete_sso_record
scope.can_delete_sso_record?(object)
end
end

View File

@ -4626,6 +4626,9 @@ en:
external_name: "Name"
external_email: "Email"
external_avatar_url: "Profile Picture URL"
last_payload: "Last Payload"
delete_sso_record: "Delete SSO Record"
confirm_delete: "Are you sure you would like to delete this single sign on (SSO) record?"
user_fields:
title: "User Fields"

View File

@ -144,6 +144,7 @@ Discourse::Application.routes.draw do
post "merge"
post "reset_bounce_score"
put "disable_second_factor"
delete "sso_record"
end
get "users/:id.json" => 'users#show', defaults: { format: 'json' }
get 'users/:id/:username' => 'users#show', constraints: { username: RouteFormat.username }

View File

@ -167,4 +167,7 @@ module UserGuardian
(is_me?(user) && user.has_trust_level?(SiteSetting.min_trust_level_to_allow_user_card_background.to_i)) || is_staff?
end
def can_delete_sso_record?(user)
SiteSetting.enable_sso && user && is_admin?
end
end

View File

@ -1049,4 +1049,17 @@ RSpec.describe Admin::UsersController do
end
end
describe '#sso_record' do
fab!(:sso_record) { SingleSignOnRecord.create!(user_id: user.id, external_id: '12345', external_email: user.email, last_payload: '') }
it "deletes the record" do
SiteSetting.sso_url = "https://www.example.com/sso"
SiteSetting.enable_sso = true
delete "/admin/users/#{user.id}/sso_record.json"
expect(response.status).to eq(200)
expect(user.single_sign_on_record).to eq(nil)
end
end
end