From 3ac64827a94d1ef42c4941a4a7890cef5ef03e8a Mon Sep 17 00:00:00 2001 From: Jiovanni Castillo <jcastillo@gitlab.com> Date: Tue, 18 Jul 2023 05:31:00 +0000 Subject: [PATCH] Add SCIM as a provider to the Users API search Changelog: added EE: true --- app/finders/users_finder.rb | 6 ++---- doc/api/users.md | 6 ++++++ ee/app/finders/ee/users_finder.rb | 8 ++++++++ ee/app/models/ee/user.rb | 2 ++ ee/spec/requests/api/users_spec.rb | 24 ++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index 13c4aae5b25c..88ba635e20b2 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -99,13 +99,11 @@ def by_active(users) users.active end - # rubocop: disable CodeReuse/ActiveRecord def by_external_identity(users) - return users unless current_user&.can_admin_all_resources? && params[:extern_uid] && params[:provider] + return users unless params[:extern_uid] && params[:provider] - users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid])) + users.by_provider_and_extern_uid(params[:provider], params[:extern_uid]) end - # rubocop: enable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord def by_external(users) diff --git a/doc/api/users.md b/doc/api/users.md index cd9c7bb578d3..d413ed361e47 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -285,6 +285,12 @@ For example: GET /users?extern_uid=1234567&provider=github ``` +Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) have the `scim` provider available: + +```plaintext +GET /users?extern_uid=1234567&provider=scim +``` + You can search users by creation date time range with: ```plaintext diff --git a/ee/app/finders/ee/users_finder.rb b/ee/app/finders/ee/users_finder.rb index 82d9375a519f..1f3180c48d3c 100644 --- a/ee/app/finders/ee/users_finder.rb +++ b/ee/app/finders/ee/users_finder.rb @@ -22,5 +22,13 @@ def by_saml_provider_id(users) users.limit_to_saml_provider(saml_provider_id) end + + override :by_external_identity + def by_external_identity(users) + return users unless params[:extern_uid] && params[:provider] + return super unless params[:provider] == "scim" + + users.with_scim_identities_by_extern_uid(params[:extern_uid]) + end end end diff --git a/ee/app/models/ee/user.rb b/ee/app/models/ee/user.rb index 849c62d2000b..3e7ef356cc82 100644 --- a/ee/app/models/ee/user.rb +++ b/ee/app/models/ee/user.rb @@ -136,6 +136,8 @@ module User where(id: ::PersonalAccessToken.with_invalid_expires_at(expiration_date).select(:user_id)) end + scope :with_scim_identities_by_extern_uid, ->(extern_uid) { joins(:scim_identities).merge(ScimIdentity.with_extern_uid(extern_uid)) } + accepts_nested_attributes_for :namespace accepts_nested_attributes_for :custom_attributes diff --git a/ee/spec/requests/api/users_spec.rb b/ee/spec/requests/api/users_spec.rb index 0ce8ec8aa2dc..a3f2df0593ac 100644 --- a/ee/spec/requests/api/users_spec.rb +++ b/ee/spec/requests/api/users_spec.rb @@ -486,4 +486,28 @@ end end end + + describe 'GET /api/users?extern_uid=:extern_uid&provider=scim' do + context 'querying users by SCIM identity as an admin' do + let(:instance_scim_user) { create(:user) } + let!(:instance_scim_identity) { create(:scim_identity, user: instance_scim_user, extern_uid: 'test_uid') } + + let(:group) { create(:group) } + let(:group_scim_user) { create(:user) } + let!(:group_scim_identity) { create(:scim_identity, user: group_scim_user, group: group, extern_uid: 'test_uid') } + let(:group_scim_user_2) { create(:user) } + let!(:group_scim_identity_2) { create(:scim_identity, user: group_scim_user_2, group: group, extern_uid: 'test_uid_2') } + + it 'returns only users for the extern_uid' do + non_scim_user = create(:user) + + get api("/users", admin, admin_mode: true), params: { extern_uid: 'test_uid', provider: 'scim' } + + expect(json_response.map { |u| u['id'] }).to include(instance_scim_user.id) + expect(json_response.map { |u| u['id'] }).to include(group_scim_user.id) + expect(json_response.map { |u| u['id'] }).not_to include(group_scim_user_2.id) + expect(json_response.map { |u| u['id'] }).not_to include(non_scim_user.id) + end + end + end end -- GitLab