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:
parent
6d7b8a71c0
commit
273db57d6e
|
@ -132,6 +132,11 @@ export default Controller.extend(CanCheckEmails, {
|
||||||
.catch(() => bootbox.alert(I18n.t("generic_error")));
|
.catch(() => bootbox.alert(I18n.t("generic_error")));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("model.single_sign_on_record.last_payload")
|
||||||
|
ssoPayload(lastPayload) {
|
||||||
|
return lastPayload.split("&");
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
impersonate() {
|
impersonate() {
|
||||||
return this.model.impersonate();
|
return this.model.impersonate();
|
||||||
|
@ -321,5 +326,16 @@ export default Controller.extend(CanCheckEmails, {
|
||||||
resetPrimaryGroup() {
|
resetPrimaryGroup() {
|
||||||
this.set("model.primary_group_id", this.originalPrimaryGroupId);
|
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();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -567,6 +567,16 @@ const AdminUser = User.extend({
|
||||||
_formatError(event) {
|
_formatError(event) {
|
||||||
return `http: ${event.status} - ${event.body}`;
|
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({
|
AdminUser.reopenClass({
|
||||||
|
|
|
@ -652,6 +652,16 @@
|
||||||
<div class="display-row">
|
<div class="display-row">
|
||||||
<div class="field">{{i18n "admin.user.sso.external_id"}}</div>
|
<div class="field">{{i18n "admin.user.sso.external_id"}}</div>
|
||||||
<div class="value">{{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>
|
||||||
<div class="display-row">
|
<div class="display-row">
|
||||||
<div class="field">{{i18n "admin.user.sso.external_username"}}</div>
|
<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="field">{{i18n "admin.user.sso.external_avatar_url"}}</div>
|
||||||
<div class="value">{{sso.external_avatar_url}}</div>
|
<div class="value">{{sso.external_avatar_url}}</div>
|
||||||
</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}}
|
{{/with}}
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
max-width: 350px;
|
max-width: 350px;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
|
word-break: break-word;
|
||||||
.select-kit {
|
.select-kit {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@ class Admin::UsersController < Admin::AdminController
|
||||||
:merge,
|
:merge,
|
||||||
:reset_bounce_score,
|
:reset_bounce_score,
|
||||||
:disable_second_factor,
|
:disable_second_factor,
|
||||||
:delete_posts_batch]
|
:delete_posts_batch,
|
||||||
|
:sso_record]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
users = ::AdminUserIndexQuery.new(params).find_users
|
users = ::AdminUserIndexQuery.new(params).find_users
|
||||||
|
@ -498,6 +499,12 @@ class Admin::UsersController < Admin::AdminController
|
||||||
render json: success_json
|
render json: success_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sso_record
|
||||||
|
guardian.ensure_can_delete_sso_record!(@user)
|
||||||
|
@user.single_sign_on_record.destroy!
|
||||||
|
render json: success_json
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def perform_post_action
|
def perform_post_action
|
||||||
|
|
|
@ -31,6 +31,7 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
||||||
:can_view_action_logs,
|
:can_view_action_logs,
|
||||||
:second_factor_enabled,
|
:second_factor_enabled,
|
||||||
:can_disable_second_factor,
|
:can_disable_second_factor,
|
||||||
|
:can_delete_sso_record,
|
||||||
:api_key_count
|
:api_key_count
|
||||||
|
|
||||||
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
|
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
|
||||||
|
@ -126,4 +127,8 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
||||||
def api_key_count
|
def api_key_count
|
||||||
object.api_keys.active.count
|
object.api_keys.active.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_delete_sso_record
|
||||||
|
scope.can_delete_sso_record?(object)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4626,6 +4626,9 @@ en:
|
||||||
external_name: "Name"
|
external_name: "Name"
|
||||||
external_email: "Email"
|
external_email: "Email"
|
||||||
external_avatar_url: "Profile Picture URL"
|
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:
|
user_fields:
|
||||||
title: "User Fields"
|
title: "User Fields"
|
||||||
|
|
|
@ -144,6 +144,7 @@ Discourse::Application.routes.draw do
|
||||||
post "merge"
|
post "merge"
|
||||||
post "reset_bounce_score"
|
post "reset_bounce_score"
|
||||||
put "disable_second_factor"
|
put "disable_second_factor"
|
||||||
|
delete "sso_record"
|
||||||
end
|
end
|
||||||
get "users/:id.json" => 'users#show', defaults: { format: 'json' }
|
get "users/:id.json" => 'users#show', defaults: { format: 'json' }
|
||||||
get 'users/:id/:username' => 'users#show', constraints: { username: RouteFormat.username }
|
get 'users/:id/:username' => 'users#show', constraints: { username: RouteFormat.username }
|
||||||
|
|
|
@ -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?
|
(is_me?(user) && user.has_trust_level?(SiteSetting.min_trust_level_to_allow_user_card_background.to_i)) || is_staff?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_delete_sso_record?(user)
|
||||||
|
SiteSetting.enable_sso && user && is_admin?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1049,4 +1049,17 @@ RSpec.describe Admin::UsersController do
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
Loading…
Reference in New Issue