[#65747] Make /scim_v2/Schemas endpoint return required by OP schemas.

https://community.openproject.org/work_packages/65747

- Make /scim_v2/Schemas endpoint return OP required schemas.
  Set required field to be required(givenName, familyName, email etc.).
  Set caseSensetive to be true where it is case sensetive.
  Remove fields that are not used by OpenProject.
  Note: making fields as requried make scimitar check their presence and
  responds with 400 which is useful.
- Refactor SCIM API controllers by extracting error handling in a general(🫡) method
- Add extra specs
- Restyle SCIM API specs a bit with hope that it makes them more readable...
This commit is contained in:
Pavel Balashou
2025-07-10 18:00:19 +02:00
parent a50fd2feea
commit b249fd36b4
8 changed files with 546 additions and 384 deletions
@@ -89,6 +89,27 @@ module ScimV2
all_possible_attributes - excluded_attributes - excluded_parents
end
def raise_result_errors_for_scim(result)
result.on_failure do |result|
uniqueness_error = result.errors.find { |e| e.type == :taken }
unauthorized_error = result.errors.find { |e| e.type == :error_unauthorized }
if uniqueness_error.present?
raise Scimitar::ErrorResponse.new(
status: 409,
scimType: "uniqueness",
detail: "Operation failed due to a uniqueness constraint: #{result.message}"
)
elsif unauthorized_error.present?
raise Scimitar::ErrorResponse.new(
status: 403,
detail: "Action forbidden: insufficient permissions."
)
else
raise result.message
end
end
end
end
end
end
+21 -29
View File
@@ -40,26 +40,15 @@ module ScimV2
call = Groups::CreateService
.new(user: User.current, model: group)
.call(group.attributes)
.on_failure do |result|
uniqueness_error = result.errors.find { |e| e.type == :taken }
if uniqueness_error.present?
raise Scimitar::ErrorResponse.new(
status: 409,
scimType: "uniqueness",
detail: "Operation failed due to a uniqueness constraint: #{result.message}"
)
else
raise result.message
end
end
raise_result_errors_for_scim(call)
group = call.result
members = scim_resource.members
if members.present?
Groups::AddUsersService
.new(group, current_user: User.system)
.call(ids: members.map(&:value), send_notifications: false)
.on_failure { |call| raise call.message }
raise_result_errors_for_scim(
Groups::AddUsersService
.new(group, current_user: User.system)
.call(ids: members.map(&:value), send_notifications: false)
)
end
group.to_scim(
@@ -75,10 +64,11 @@ module ScimV2
storage_class.transaction do
group = storage_scope.find(group_id)
group.from_scim!(scim_hash: scim_resource.as_json)
Groups::UpdateService
.new(user: User.current, model: group)
.call(user_ids: scim_resource.members.map(&:value))
.on_failure { |call| raise call.message }
raise_result_errors_for_scim(
Groups::UpdateService
.new(user: User.current, model: group)
.call(user_ids: scim_resource.members.map(&:value))
)
group.reload
group.to_scim(
location: url_for(action: :show, id: group.id),
@@ -94,10 +84,11 @@ module ScimV2
group = storage_scope.find(group_id)
group.from_scim_patch!(patch_hash: patch_hash)
user_ids = group.scim_members.map(&:id)
Groups::UpdateService
.new(user: User.current, model: group)
.call(user_ids:)
.on_failure { |call| raise call.message }
raise_result_errors_for_scim(
Groups::UpdateService
.new(user: User.current, model: group)
.call(user_ids:)
)
group.reload
group.to_scim(
location: url_for(action: :show, id: group.id),
@@ -110,10 +101,11 @@ module ScimV2
def destroy
super do |group_id|
group = storage_scope.find(group_id)
Groups::DeleteService
.new(user: User.current, model: group)
.call
.on_failure { |call| raise call.message }
raise_result_errors_for_scim(
Groups::DeleteService
.new(user: User.current, model: group)
.call
)
end
end
+15 -35
View File
@@ -40,19 +40,7 @@ module ScimV2
call = Users::CreateService
.new(user: User.current, model: user)
.call(user.attributes)
.on_failure do |result|
uniqueness_error = result.errors.find { |e| e.type == :taken }
if uniqueness_error.present?
raise Scimitar::ErrorResponse.new(
status: 409,
scimType: "uniqueness",
detail: "Operation failed due to a uniqueness constraint: #{result.message}"
)
else
raise result.message
end
end
raise_result_errors_for_scim(call)
user = call.result
user.to_scim(
location: url_for(action: :show, id: user.id),
@@ -67,10 +55,11 @@ module ScimV2
storage_class.transaction do
user = storage_scope.find(user_id)
user.from_scim!(scim_hash: scim_resource.as_json)
Users::UpdateService
.new(user: User.current, model: user)
.call
.on_failure { |call| raise call.message }
call = Users::UpdateService
.new(user: User.current, model: user)
.call
raise_result_errors_for_scim(call)
user = call.result
user.to_scim(
location: url_for(action: :show, id: user.id),
include_attributes:
@@ -84,10 +73,11 @@ module ScimV2
storage_class.transaction do
user = storage_scope.find(user_id)
user.from_scim_patch!(patch_hash: patch_hash)
Users::UpdateService
.new(user: User.current, model: user)
.call
.on_failure { |call| raise call.message }
call = Users::UpdateService
.new(user: User.current, model: user)
.call
raise_result_errors_for_scim(call)
user = call.result
user.to_scim(
location: url_for(action: :show, id: user.id),
include_attributes:
@@ -99,20 +89,10 @@ module ScimV2
def destroy
super do |user_id|
user = storage_scope.find(user_id)
Users::DeleteService
.new(user: User.current, model: user)
.call
.on_failure do |result|
unauthorized_error = result.errors.find { |e| e.type == :error_unauthorized }
if unauthorized_error.present?
raise Scimitar::ErrorResponse.new(
status: 403,
detail: "User can't be deleted due to permission absence."
)
else
raise result.message
end
end
call = Users::DeleteService
.new(user: User.current, model: user)
.call
raise_result_errors_for_scim(call)
end
end
+2 -2
View File
@@ -38,10 +38,10 @@ Rails.application.config.to_prepare do
)
Scimitar::Schema::User.singleton_class.class_eval do
prepend ScimitarSchemaExtension
prepend ScimitarSchemaExtension::User
end
Scimitar::Schema::Group.singleton_class.class_eval do
prepend ScimitarSchemaExtension
prepend ScimitarSchemaExtension::Group
end
end
+38 -6
View File
@@ -28,11 +28,43 @@
# See COPYRIGHT and LICENSE files for more details.
#++
module ScimitarSchemaExtension
def scim_attributes
super + [Scimitar::Schema::Attribute.new(name: "externalId",
type: "string",
caseExact: true,
required: true)]
class OpenProjectNameSchema < Scimitar::Schema::Base
def self.scim_attributes
@scim_attributes ||= [
Scimitar::Schema::Attribute.new(name: "familyName", caseExact: true, type: "string", required: true),
Scimitar::Schema::Attribute.new(name: "givenName", caseExact: true, type: "string", required: true)
]
end
end
class OpenProjectNameComplexType < Scimitar::ComplexTypes::Base
set_schema OpenProjectNameSchema
end
module ScimitarSchemaExtension
module Group
def scim_attributes
[
Scimitar::Schema::Attribute.new(name: "displayName", caseExact: true, type: "string", required: true),
Scimitar::Schema::Attribute.new(name: "members", multiValued: true, complexType: Scimitar::ComplexTypes::ReferenceMember,
mutability: "readWrite"),
Scimitar::Schema::Attribute.new(name: "externalId", type: "string", caseExact: true, required: true)
]
end
end
module User
def scim_attributes
[
Scimitar::Schema::Attribute.new(name: "userName", caseExact: true, type: "string", uniqueness: "server", required: true),
Scimitar::Schema::Attribute.new(name: "name", caseExact: true, complexType: OpenProjectNameComplexType, required: true),
Scimitar::Schema::Attribute.new(name: "active", type: "boolean"),
Scimitar::Schema::Attribute.new(name: "emails", multiValued: true, complexType: Scimitar::ComplexTypes::Email,
required: true),
Scimitar::Schema::Attribute.new(name: "groups", multiValued: true, complexType: Scimitar::ComplexTypes::ReferenceGroup,
mutability: "readOnly"),
Scimitar::Schema::Attribute.new(name: "externalId", type: "string", caseExact: true, required: true)
]
end
end
end
+157 -151
View File
@@ -61,26 +61,26 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
get "/scim_v2/Groups", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to match({ "Resources" => contain_exactly({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "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" => 2 })
expect(response_body).to match("Resources" => contain_exactly({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "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" => 2)
end
it "filters results" do
@@ -88,29 +88,29 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
get "/scim_v2/Groups?filter=#{filter}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "Resources" => [{ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.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 })
expect(response_body).to eq("Resources" => [{ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.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)
filter = ERB::Util.url_encode('displayName Eq "NONEXISTENT GROUP NAME"')
get "/scim_v2/Groups?filter=#{filter}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "Resources" => [],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 0 })
expect(response_body).to eq("Resources" => [],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 0)
end
end
@@ -120,8 +120,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -135,29 +136,29 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
get "/scim_v2/Groups/#{group.id}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
it "excludes specified attributes" do
get "/scim_v2/Groups/#{group.id}?excludedAttributes=members", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
expect(response_body["members"]).to be_nil
end
end
@@ -168,8 +169,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -192,15 +194,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group = Group.find_by(name: group_name)
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
it "creates group without members specified" do
@@ -214,15 +216,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group = Group.find_by(name: group_name)
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
end
@@ -232,8 +234,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -252,15 +255,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
get "/scim_v2/Groups/#{group.id}", "", headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
perform_enqueued_jobs
assert_performed_jobs 1
@@ -269,9 +272,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Resource \"#{group.id}\" not found",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "404" }
"detail" => "Resource \"#{group.id}\" not found",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "404"
)
end
end
@@ -282,8 +285,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -311,15 +315,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group.reload
expect(response_body).to match({ "displayName" => group.name,
"externalId" => new_external_group_id,
"id" => group.id.to_s,
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"members" => contain_exactly({ "value" => user.id.to_s }, { "value" => admin.id.to_s }),
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to match("displayName" => group.name,
"externalId" => new_external_group_id,
"id" => group.id.to_s,
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"members" => contain_exactly({ "value" => user.id.to_s }, { "value" => admin.id.to_s }),
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
end
@@ -329,8 +333,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -355,15 +360,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group.reload
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => new_external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => new_external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
it "supports replacing of members" do
@@ -385,15 +390,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group.reload
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user2.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user2.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
it "supports adding of a member" do
@@ -413,16 +418,16 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group.reload
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user1.id.to_s },
{ "value" => user2.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [{ "value" => user1.id.to_s },
{ "value" => user2.id.to_s }],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
it "supports removal of a member" do
@@ -441,15 +446,15 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group.reload
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"members" => [],
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
it "supports removal of a member with exclusion of members list from the response" do
@@ -468,14 +473,14 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
group.reload
expect(response_body).to eq({ "displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] })
expect(response_body).to eq("displayName" => group.name,
"externalId" => external_group_id,
"id" => group.id.to_s,
"meta" => { "location" => "http://test.host/scim_v2/Groups/#{group.id}",
"created" => group.created_at.iso8601,
"lastModified" => group.updated_at.iso8601,
"resourceType" => "Group" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
end
@@ -485,8 +490,9 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
end
end
+81 -13
View File
@@ -42,26 +42,93 @@ RSpec.describe "SCIM API Schemas", with_ee: [:scim_api] do
describe "GET /scim_v2/Schemas" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
# rubocop:disable Layout/LineLength
it "responds with supported schemas" do
get "/scim_v2/Schemas", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body["totalResults"]).to eq(2)
expect(response_body["schemas"]).to eq(["urn:ietf:params:scim:api:messages:2.0:ListResponse"])
expect(response_body["schemas"]).to eq(["urn:ietf:params:scim:api:messages:2.0:ListResponse"])
group_schema = response_body["Resources"].find { |r| r["name"] == "Group" }
user_schema = response_body["Resources"].find { |r| r["name"] == "User" }
external_id_schema = { "multiValued" => false,
"required" => true,
"caseExact" => true,
"mutability" => "readWrite",
"uniqueness" => "none",
"returned" => "default",
"name" => "externalId",
"type" => "string" }
expect(group_schema["attributes"]).to include(external_id_schema)
expect(user_schema["attributes"]).to include(external_id_schema)
expect(group_schema).to eq(
"name" => "Group",
"id" => "urn:ietf:params:scim:schemas:core:2.0:Group",
"description" => "Represents a Group",
"meta" => { "resourceType" => "Schema",
"location" => "http://test.host/scim_v2/Schemas?name=urn%3Aietf%3Aparams%3Ascim%3Aschemas%3Acore%3A2.0%3AGroup" },
"attributes" => [
{ "multiValued" => false, "required" => true, "caseExact" => true, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "displayName", "type" => "string" },
{ "multiValued" => true,
"required" => false,
"caseExact" => false,
"mutability" => "readWrite",
"uniqueness" => "none",
"returned" => "default",
"type" => "complex",
"subAttributes" => [
{ "multiValued" => false, "required" => true, "caseExact" => false, "mutability" => "immutable", "uniqueness" => "none", "returned" => "default", "name" => "value", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "immutable", "uniqueness" => "none", "returned" => "default", "name" => "type", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "immutable", "uniqueness" => "none", "returned" => "default", "name" => "display", "type" => "string" }
],
"name" => "members" },
{ "multiValued" => false, "required" => true, "caseExact" => true, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "externalId", "type" => "string" }
]
)
expect(user_schema).to eq(
"name" => "User",
"id" => "urn:ietf:params:scim:schemas:core:2.0:User",
"description" => "Represents a User",
"meta" => { "resourceType" => "Schema",
"location" => "http://test.host/scim_v2/Schemas?name=urn%3Aietf%3Aparams%3Ascim%3Aschemas%3Acore%3A2.0%3AUser" },
"attributes" => [
{ "multiValued" => false, "required" => true, "caseExact" => true, "mutability" => "readWrite", "uniqueness" => "server", "returned" => "default", "name" => "userName", "type" => "string" },
{ "multiValued" => false,
"required" => true,
"caseExact" => true,
"mutability" => "readWrite",
"uniqueness" => "none",
"returned" => "default",
"type" => "complex",
"subAttributes" => [
{ "multiValued" => false, "required" => true, "caseExact" => true, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "familyName", "type" => "string" },
{ "multiValued" => false, "required" => true, "caseExact" => true, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "givenName", "type" => "string" }
],
"name" => "name" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "active", "type" => "boolean" },
{ "multiValued" => true,
"required" => true,
"caseExact" => false,
"mutability" => "readWrite",
"uniqueness" => "none",
"returned" => "default",
"type" => "complex",
"subAttributes" => [
{ "multiValued" => false, "required" => true, "caseExact" => false, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "value", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "readOnly", "uniqueness" => "none", "returned" => "default", "name" => "display", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "type", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "primary", "type" => "boolean" }
],
"name" => "emails" },
{ "multiValued" => true,
"required" => false,
"caseExact" => false,
"mutability" => "readOnly",
"uniqueness" => "none",
"returned" => "default",
"type" => "complex",
"subAttributes" => [
{ "multiValued" => false, "required" => true, "caseExact" => false, "mutability" => "readOnly", "uniqueness" => "none", "returned" => "default", "name" => "value", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "readOnly", "uniqueness" => "none", "returned" => "default", "name" => "display", "type" => "string" },
{ "multiValued" => false, "required" => false, "caseExact" => false, "mutability" => "readOnly", "uniqueness" => "none", "returned" => "default", "name" => "type", "type" => "string" }
],
"name" => "groups" },
{ "multiValued" => false, "required" => true, "caseExact" => true, "mutability" => "readWrite", "uniqueness" => "none", "returned" => "default", "name" => "externalId", "type" => "string" }
]
)
end
# rubocop:enable Layout/LineLength
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
@@ -70,8 +137,9 @@ RSpec.describe "SCIM API Schemas", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
end
end
+211 -148
View File
@@ -122,11 +122,11 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
get "/scim_v2/Users?filter=#{filter_with_nonexisting_rows}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "Resources" => [],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 0 })
expect(response_body).to eq("Resources" => [],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 0)
end
it "filters results by externalId" do
@@ -158,11 +158,11 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
get "/scim_v2/Users?filter=#{filter_with_nonexisting_rows}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "Resources" => [],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 0 })
expect(response_body).to eq("Resources" => [],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 0)
end
end
@@ -172,8 +172,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -187,21 +188,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
get "/scim_v2/Users/#{user.id}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "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 })
expect(response_body).to eq("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)
end
end
@@ -211,8 +212,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -247,14 +249,71 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
expect(last_response).to have_http_status(409)
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail" => "Operation failed due to a uniqueness constraint: Username has already been taken.",
"status" => "409",
"scimType" => "uniqueness" }
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail" => "Operation failed due to a uniqueness constraint: Username has already been taken.",
"status" => "409",
"scimType" => "uniqueness"
)
end
end
it "responds with 400 when email is missing" do
group
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId" => external_user_id,
"userName" => user.login,
"name" => {
"givenName" => "John",
"familyName" => "Doe"
},
"active" => true,
"emails" => []
}
post "/scim_v2/Users/", request_body.to_json, headers
expect(last_response).to have_http_status(400)
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail" => "Invalid resource: Emails is required.",
"status" => "400",
"scimType" => "invalidValue"
)
end
it "responds with 400 when familyName is missing" do
group
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId" => external_user_id,
"userName" => user.login,
"name" => {
"givenName" => "John"
},
"active" => true,
"emails" => [
{
"value" => "jdoe@example.com",
"type" => "work",
"primary" => true
}
]
}
post "/scim_v2/Users/", request_body.to_json, headers
expect(last_response).to have_http_status(400)
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail" => "Invalid resource: Name familyname is required.",
"status" => "400",
"scimType" => "invalidValue"
)
end
it "creates user with provided data and excludes some attributes" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
@@ -278,17 +337,17 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
created_user = User.find_by(login: "jdoe")
expect(created_user).to be_present
expect(response_body).to eq({ "active" => true,
"externalId" => external_user_id,
"groups" => [],
"id" => created_user.id.to_s,
"meta" => { "created" => created_user.created_at.iso8601,
"lastModified" => created_user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{created_user.id}",
"resourceType" => "User" },
"name" => { "familyName" => "Doe" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => "jdoe" })
expect(response_body).to eq("active" => true,
"externalId" => external_user_id,
"groups" => [],
"id" => created_user.id.to_s,
"meta" => { "created" => created_user.created_at.iso8601,
"lastModified" => created_user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{created_user.id}",
"resourceType" => "User" },
"name" => { "familyName" => "Doe" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => "jdoe")
end
it "creates user with provided data" do
@@ -314,21 +373,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
created_user = User.find_by(login: "jdoe")
expect(created_user).to be_present
expect(response_body).to eq({ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => "jdoe@example.com" }],
"externalId" => external_user_id,
"groups" => [],
"id" => created_user.id.to_s,
"meta" => { "created" => created_user.created_at.iso8601,
"lastModified" => created_user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{created_user.id}",
"resourceType" => "User" },
"name" => { "familyName" => "Doe",
"givenName" => "John" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => "jdoe" })
expect(response_body).to eq("active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => "jdoe@example.com" }],
"externalId" => external_user_id,
"groups" => [],
"id" => created_user.id.to_s,
"meta" => { "created" => created_user.created_at.iso8601,
"lastModified" => created_user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{created_user.id}",
"resourceType" => "User" },
"name" => { "familyName" => "Doe",
"givenName" => "John" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => "jdoe")
end
it "creates user with any email type string provided" do
@@ -352,21 +411,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
created_user = User.find_by(login: "jdoe")
expect(response_body).to eq({ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => "jdoe@example.com" }],
"externalId" => external_user_id,
"groups" => [],
"id" => created_user.id.to_s,
"meta" => { "created" => created_user.created_at.iso8601,
"lastModified" => created_user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{created_user.id}",
"resourceType" => "User" },
"name" => { "familyName" => "Doe",
"givenName" => "John" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => "jdoe" })
expect(response_body).to eq("active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => "jdoe@example.com" }],
"externalId" => external_user_id,
"groups" => [],
"id" => created_user.id.to_s,
"meta" => { "created" => created_user.created_at.iso8601,
"lastModified" => created_user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{created_user.id}",
"resourceType" => "User" },
"name" => { "familyName" => "Doe",
"givenName" => "John" },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => "jdoe")
end
end
@@ -376,8 +435,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -398,21 +458,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
get "/scim_v2/Users/#{user.id}", "", headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "active" => false,
"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 })
expect(response_body).to eq("active" => false,
"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)
perform_enqueued_jobs
assert_performed_jobs 1
@@ -421,9 +481,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Resource \"#{user.id}\" not found",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "404" }
"detail" => "Resource \"#{user.id}\" not found",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "404"
)
end
end
@@ -434,9 +494,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
delete "/scim_v2/Users/#{user.id}", "", headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq({ "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail" => "User can't be deleted due to permission absence.",
"status" => "403" })
expect(response_body).to eq("schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail" => "Action forbidden: insufficient permissions.",
"status" => "403")
expect(last_response).to have_http_status(403)
end
end
@@ -448,8 +508,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -485,21 +546,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
user.reload
expect(response_body).to eq({ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => request_body["emails"].first["value"] }],
"externalId" => new_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" => request_body["name"]["familyName"],
"givenName" => request_body["name"]["givenName"] },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => request_body["userName"] })
expect(response_body).to eq("active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => request_body["emails"].first["value"] }],
"externalId" => new_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" => request_body["name"]["familyName"],
"givenName" => request_body["name"]["givenName"] },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => request_body["userName"])
end
end
@@ -509,8 +570,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
@@ -537,21 +599,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
user.reload
expect(response_body).to eq({ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => user.mail }],
"externalId" => new_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 })
expect(response_body).to eq("active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => user.mail }],
"externalId" => new_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)
end
it "changes email value" do
@@ -574,21 +636,21 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
user.reload
expect(response_body).to eq({ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => new_email_value }],
"externalId" => user.scim_external_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 })
expect(response_body).to eq("active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => new_email_value }],
"externalId" => user.scim_external_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)
end
end
@@ -598,8 +660,9 @@ RSpec.describe "SCIM API Users", with_ee: [:scim_api] do
response_body = JSON.parse(last_response.body)
expect(response_body).to eq(
{ "detail" => "Requires authentication", "schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401" }
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end