From 273db57d6e08e6435e8883c76ef9ba8d2801df5f Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Tue, 15 Sep 2020 10:00:10 -0400 Subject: [PATCH] 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. --- .../admin/controllers/admin-user-index.js | 16 +++++++++++++++ .../javascripts/admin/models/admin-user.js | 10 ++++++++++ .../admin/templates/user-index.hbs | 20 +++++++++++++++++++ .../stylesheets/common/admin/users.scss | 1 + app/controllers/admin/users_controller.rb | 9 ++++++++- .../admin_detailed_user_serializer.rb | 5 +++++ config/locales/client.en.yml | 3 +++ config/routes.rb | 1 + lib/guardian/user_guardian.rb | 3 +++ spec/requests/admin/users_controller_spec.rb | 13 ++++++++++++ 10 files changed, 80 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/controllers/admin-user-index.js b/app/assets/javascripts/admin/controllers/admin-user-index.js index 92101bce2f2..8f58755ef06 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-index.js +++ b/app/assets/javascripts/admin/controllers/admin-user-index.js @@ -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(); + } + ); + }, }, }); diff --git a/app/assets/javascripts/admin/models/admin-user.js b/app/assets/javascripts/admin/models/admin-user.js index 5882910093d..8d4b38baeaa 100644 --- a/app/assets/javascripts/admin/models/admin-user.js +++ b/app/assets/javascripts/admin/models/admin-user.js @@ -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({ diff --git a/app/assets/javascripts/admin/templates/user-index.hbs b/app/assets/javascripts/admin/templates/user-index.hbs index 5cccf43e373..c043ce529f1 100644 --- a/app/assets/javascripts/admin/templates/user-index.hbs +++ b/app/assets/javascripts/admin/templates/user-index.hbs @@ -652,6 +652,16 @@
{{i18n "admin.user.sso.external_id"}}
{{sso.external_id}}
+ {{#if model.can_delete_sso_record}} +
+ {{d-button + class="btn-danger" + action=(action "deleteSSORecord") + icon="far-trash-alt" + label="admin.user.sso.delete_sso_record" + }} +
+ {{/if}}
{{i18n "admin.user.sso.external_username"}}
@@ -671,6 +681,16 @@
{{i18n "admin.user.sso.external_avatar_url"}}
{{sso.external_avatar_url}}
+ {{#if sso.last_payload}} +
+
{{i18n "admin.user.sso.last_payload"}}
+
+ {{#each ssoPayload as |line|}} + {{line}}
+ {{/each}} +
+
+ {{/if}} {{/with}} {{/if}} diff --git a/app/assets/stylesheets/common/admin/users.scss b/app/assets/stylesheets/common/admin/users.scss index dcc0b0e3d9e..28a8612c01c 100644 --- a/app/assets/stylesheets/common/admin/users.scss +++ b/app/assets/stylesheets/common/admin/users.scss @@ -49,6 +49,7 @@ max-width: 350px; min-width: 50px; margin-left: 12px; + word-break: break-word; .select-kit { min-width: 100px; } diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 0deed95a8ab..48b292cc1c5 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -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 diff --git a/app/serializers/admin_detailed_user_serializer.rb b/app/serializers/admin_detailed_user_serializer.rb index 0f3840ed8ee..6dc9413ae41 100644 --- a/app/serializers/admin_detailed_user_serializer.rb +++ b/app/serializers/admin_detailed_user_serializer.rb @@ -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 diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e630d466b79..6e3878515fb 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -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" diff --git a/config/routes.rb b/config/routes.rb index 47bc641eb5f..342ced7813f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 } diff --git a/lib/guardian/user_guardian.rb b/lib/guardian/user_guardian.rb index aa917791849..6e2ccdb03cd 100644 --- a/lib/guardian/user_guardian.rb +++ b/lib/guardian/user_guardian.rb @@ -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 diff --git a/spec/requests/admin/users_controller_spec.rb b/spec/requests/admin/users_controller_spec.rb index 14a6cb0196b..83bd456dfbe 100644 --- a/spec/requests/admin/users_controller_spec.rb +++ b/spec/requests/admin/users_controller_spec.rb @@ -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