mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
[#62107] Update SCIM Server API.
- Use ServiceAccount associated with ScimClient for making user changes - Remove scoping by scim_client.auth_provider_id So, SCIM Client has access to any not_builtin User. - Associate user with AuthProvider configured in ScimModel instead of choosing the first one.
This commit is contained in:
@@ -38,7 +38,7 @@ module ScimV2
|
||||
group = storage_class.new
|
||||
group.from_scim!(scim_hash: scim_resource.as_json)
|
||||
call = Groups::CreateService
|
||||
.new(user: User.system, model: group)
|
||||
.new(user: User.current, model: group)
|
||||
.call(group.attributes)
|
||||
.on_failure do |result|
|
||||
uniqueness_error = result.errors.find { |e| e.type == :taken }
|
||||
@@ -73,7 +73,7 @@ module ScimV2
|
||||
group = storage_scope.find(group_id)
|
||||
group.from_scim!(scim_hash: scim_resource.as_json)
|
||||
Groups::UpdateService
|
||||
.new(user: User.system, model: group)
|
||||
.new(user: User.current, model: group)
|
||||
.call(user_ids: scim_resource.members.map(&:value))
|
||||
.on_failure { |call| raise call.message }
|
||||
group.reload
|
||||
@@ -89,7 +89,7 @@ module ScimV2
|
||||
group.from_scim_patch!(patch_hash: patch_hash)
|
||||
user_ids = group.scim_members.map(&:id)
|
||||
Groups::UpdateService
|
||||
.new(user: User.system, model: group)
|
||||
.new(user: User.current, model: group)
|
||||
.call(user_ids:)
|
||||
.on_failure { |call| raise call.message }
|
||||
group.reload
|
||||
@@ -102,7 +102,7 @@ module ScimV2
|
||||
super do |group_id|
|
||||
group = storage_scope.find(group_id)
|
||||
Groups::DeleteService
|
||||
.new(user: User.system, model: group)
|
||||
.new(user: User.current, model: group)
|
||||
.call
|
||||
.on_failure { |call| raise call.message }
|
||||
end
|
||||
@@ -118,12 +118,7 @@ module ScimV2
|
||||
Group
|
||||
.left_joins(:users, :user_auth_provider_links)
|
||||
.includes(:users, :user_auth_provider_links)
|
||||
.where(user_auth_provider_links: { auth_provider_id: scim_client.auth_provider_id })
|
||||
.not_builtin
|
||||
end
|
||||
|
||||
def scim_client
|
||||
User.current.service_account_association.service
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ module ScimV2
|
||||
user.firstname = "123" if user.firstname.blank?
|
||||
user.lastname = "456" if user.lastname.blank?
|
||||
call = Users::CreateService
|
||||
.new(user: User.system, model: user)
|
||||
.new(user: User.current, model: user)
|
||||
.call(user.attributes)
|
||||
.on_failure do |result|
|
||||
uniqueness_error = result.errors.find { |e| e.type == :taken }
|
||||
@@ -67,7 +67,7 @@ module ScimV2
|
||||
user = storage_scope.find(user_id)
|
||||
user.from_scim!(scim_hash: scim_resource.as_json)
|
||||
Users::UpdateService
|
||||
.new(user: User.system, model: user)
|
||||
.new(user: User.current, model: user)
|
||||
.call
|
||||
.on_failure { |call| raise call.message }
|
||||
user.to_scim(location: url_for(action: :show, id: user.id))
|
||||
@@ -81,7 +81,7 @@ module ScimV2
|
||||
user = storage_scope.find(user_id)
|
||||
user.from_scim_patch!(patch_hash: patch_hash)
|
||||
Users::UpdateService
|
||||
.new(user: User.system, model: user)
|
||||
.new(user: User.current, model: user)
|
||||
.call
|
||||
.on_failure { |call| raise call.message }
|
||||
user.to_scim(location: url_for(action: :show, id: user.id))
|
||||
@@ -93,7 +93,7 @@ module ScimV2
|
||||
super do |user_id|
|
||||
user = storage_scope.find(user_id)
|
||||
Users::DeleteService
|
||||
.new(user: User.system, model: user)
|
||||
.new(user: User.current, model: user)
|
||||
.call
|
||||
.on_failure { |call| raise call.message }
|
||||
end
|
||||
@@ -109,12 +109,7 @@ module ScimV2
|
||||
User
|
||||
.left_joins(:groups, :user_auth_provider_links)
|
||||
.includes(:groups, :user_auth_provider_links)
|
||||
.where(user_auth_provider_links: { auth_provider_id: scim_client.auth_provider_id })
|
||||
.not_builtin
|
||||
end
|
||||
|
||||
def scim_client
|
||||
User.current.service_account_association.service
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+1
-2
@@ -74,8 +74,7 @@ class Group < Principal
|
||||
end
|
||||
|
||||
def scim_external_id=(external_id)
|
||||
oidc_provider = OpenIDConnect::Provider.first
|
||||
raise "There should at least one OIDC Provider for SCIM to work with" unless oidc_provider
|
||||
oidc_provider = User.current.service_account_association.service.auth_provider
|
||||
|
||||
::Groups::SetAttributesService
|
||||
.new(user: User.system, model: self, contract_class: EmptyContract)
|
||||
|
||||
+1
-2
@@ -563,8 +563,7 @@ class User < Principal
|
||||
end
|
||||
|
||||
def scim_external_id=(external_id)
|
||||
oidc_provider = OpenIDConnect::Provider.first
|
||||
raise "There should at least one OIDC Provider for SCIM to work with" unless oidc_provider
|
||||
oidc_provider = User.current.service_account_association.service.auth_provider
|
||||
|
||||
::Users::SetAttributesService
|
||||
.new(user: User.system, model: self, contract_class: EmptyContract)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
FactoryBot.define do
|
||||
factory :user, parent: :principal, class: "User" do
|
||||
firstname { "Bob" }
|
||||
lastname { "Bobbit" }
|
||||
sequence(:lastname) { |n| "Bobbit#{n}" }
|
||||
sequence(:login) { |n| "bob#{n}" }
|
||||
sequence(:mail) { |n| "bobmail#{n}.bobbit@bob.com" }
|
||||
password { "adminADMIN!" }
|
||||
|
||||
@@ -44,7 +44,7 @@ RSpec.describe "SCIM API Groups" do
|
||||
let(:group_without_external_id) { create(:group, members: [user]) }
|
||||
let(:headers) { { "CONTENT_TYPE" => "application/scim+json", "HTTP_AUTHORIZATION" => "Bearer #{token.plaintext_token}" } }
|
||||
let(:token) { create(:oauth_access_token, resource_owner: service_account, scopes: ["scim_v2"]) }
|
||||
let(:service_account) { create(:service_account, service: scim_client) }
|
||||
let(:service_account) { create(:service_account, service: scim_client, admin: true) }
|
||||
let(:scim_client) { create(:scim_client, authentication_method: :oauth2_token, auth_provider_id: oidc_provider.id) }
|
||||
|
||||
before do
|
||||
@@ -61,7 +61,7 @@ RSpec.describe "SCIM API Groups" do
|
||||
get "/scim_v2/Groups", {}, headers
|
||||
|
||||
response_body = JSON.parse(last_response.body)
|
||||
expect(response_body).to eq({ "Resources" => [{ "displayName" => group.name,
|
||||
expect(response_body).to match({ "Resources" => match_array([{ "displayName" => group.name,
|
||||
"externalId" => external_group_id,
|
||||
"id" => group.id.to_s,
|
||||
"members" => [{ "value" => user.id.to_s }],
|
||||
@@ -69,11 +69,20 @@ RSpec.describe "SCIM API Groups" do
|
||||
"created" => group.created_at.iso8601,
|
||||
"lastModified" => group.updated_at.iso8601,
|
||||
"resourceType" => "Group" },
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] }],
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] },
|
||||
{ "displayName" => group_without_external_id.name,
|
||||
"id" => group_without_external_id.id.to_s,
|
||||
"members" => [{ "value" => user.id.to_s }],
|
||||
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group_without_external_id.id}",
|
||||
"created" => group_without_external_id.created_at.iso8601,
|
||||
"lastModified" => group_without_external_id.updated_at.iso8601,
|
||||
"resourceType" => "Group" },
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] }
|
||||
]),
|
||||
"itemsPerPage" => 100,
|
||||
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||
"startIndex" => 1,
|
||||
"totalResults" => 1 })
|
||||
"totalResults" => 2 })
|
||||
end
|
||||
|
||||
it "filters results" do
|
||||
|
||||
@@ -41,7 +41,7 @@ RSpec.describe "SCIM API Users" do
|
||||
let(:group) { create(:group, identity_url: "#{oidc_provider.slug}:#{external_group_id}", members: [user]) }
|
||||
let(:headers) { { "CONTENT_TYPE" => "application/scim+json", "HTTP_AUTHORIZATION" => "Bearer #{token.plaintext_token}" } }
|
||||
let(:token) { create(:oauth_access_token, resource_owner: service_account, scopes: ["scim_v2"]) }
|
||||
let(:service_account) { create(:service_account, service: scim_client) }
|
||||
let(:service_account) { create(:service_account, service: scim_client, admin: true) }
|
||||
let(:scim_client) { create(:scim_client, authentication_method: :oauth2_token, auth_provider_id: oidc_provider.id) }
|
||||
|
||||
before { token }
|
||||
@@ -57,41 +57,40 @@ RSpec.describe "SCIM API Users" do
|
||||
get "/scim_v2/Users", {}, headers
|
||||
|
||||
response_body = JSON.parse(last_response.body)
|
||||
expect(response_body).to eq("Resources" =>
|
||||
[{ "active" => true,
|
||||
"emails" => [{ "primary" => true,
|
||||
"type" => "work",
|
||||
"value" => admin.mail }],
|
||||
"groups" => [],
|
||||
"externalId" => external_admin_id,
|
||||
"id" => admin.id.to_s,
|
||||
"meta" => { "created" => admin.created_at.iso8601,
|
||||
"lastModified" => admin.updated_at.iso8601,
|
||||
"location" => "http://test.host/scim_v2/Users/#{admin.id}",
|
||||
"resourceType" => "User" },
|
||||
"name" => { "familyName" => admin.lastname,
|
||||
"givenName" => admin.firstname },
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||
"userName" => admin.login },
|
||||
{ "active" => true,
|
||||
"emails" => [{ "primary" => true,
|
||||
"type" => "work",
|
||||
"value" => user.mail }],
|
||||
"externalId" => external_user_id,
|
||||
"groups" => [{ "value" => group.id.to_s }],
|
||||
"id" => user.id.to_s,
|
||||
"meta" => { "created" => user.created_at.iso8601,
|
||||
"lastModified" => user.updated_at.iso8601,
|
||||
"location" => "http://test.host/scim_v2/Users/#{user.id}",
|
||||
"resourceType" => "User" },
|
||||
"name" => { "familyName" => user.lastname,
|
||||
"givenName" => user.firstname },
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||
"userName" => user.login }],
|
||||
"itemsPerPage" => 100,
|
||||
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||
"startIndex" => 1,
|
||||
"totalResults" => 2)
|
||||
expect(response_body).to match("Resources" => include({ "active" => true,
|
||||
"emails" => [{ "primary" => true,
|
||||
"type" => "work",
|
||||
"value" => admin.mail }],
|
||||
"groups" => [],
|
||||
"externalId" => external_admin_id,
|
||||
"id" => admin.id.to_s,
|
||||
"meta" => { "created" => admin.created_at.iso8601,
|
||||
"lastModified" => admin.updated_at.iso8601,
|
||||
"location" => "http://test.host/scim_v2/Users/#{admin.id}",
|
||||
"resourceType" => "User" },
|
||||
"name" => { "familyName" => admin.lastname,
|
||||
"givenName" => admin.firstname },
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||
"userName" => admin.login },
|
||||
{ "active" => true,
|
||||
"emails" => [{ "primary" => true,
|
||||
"type" => "work",
|
||||
"value" => user.mail }],
|
||||
"externalId" => external_user_id,
|
||||
"groups" => [{ "value" => group.id.to_s }],
|
||||
"id" => user.id.to_s,
|
||||
"meta" => { "created" => user.created_at.iso8601,
|
||||
"lastModified" => user.updated_at.iso8601,
|
||||
"location" => "http://test.host/scim_v2/Users/#{user.id}",
|
||||
"resourceType" => "User" },
|
||||
"name" => { "familyName" => user.lastname,
|
||||
"givenName" => user.firstname },
|
||||
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||
"userName" => user.login }),
|
||||
"itemsPerPage" => 100,
|
||||
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||
"startIndex" => 1,
|
||||
"totalResults" => 4)
|
||||
end
|
||||
|
||||
it "filters results by familyName" do
|
||||
@@ -345,7 +344,7 @@ RSpec.describe "SCIM API Users" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /scim_v2/Users/:id" do
|
||||
describe "DELETE /scim_v2/Users/:id", with_settings: { users_deletable_by_admins: true } do
|
||||
context "with the feature flag enabled", with_flag: { scim_api: true } do
|
||||
it do
|
||||
group
|
||||
|
||||
Reference in New Issue
Block a user