mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
[#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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user