Remove feature toggle for SCIM API

This has been part of OpenProject for a few releases
already and there is no need to disable it anymore.
This commit is contained in:
Jan Sandbrink
2025-12-04 13:10:51 +01:00
parent cc75f5ded4
commit 99ddb6752f
8 changed files with 1068 additions and 1313 deletions
-4
View File
@@ -53,10 +53,6 @@ OpenProject::FeatureDecisions.add :oidc_group_sync,
description: "Allows to synchronize groups from OpenID Connect providers",
force_active: true
OpenProject::FeatureDecisions.add :scim_api,
description: "Enables SCIM API.",
force_active: true
OpenProject::FeatureDecisions.add :beta_widgets,
description: "Enables BETA versions of widgets."
+1 -1
View File
@@ -588,7 +588,7 @@ Redmine::MenuManager.map :admin_menu do |menu|
menu.push :scim_clients,
{ controller: "/admin/scim_clients", action: "index" },
if: ->(_) { User.current.admin? && OpenProject::FeatureDecisions.scim_api_active? },
if: ->(_) { User.current.admin? },
parent: :authentication,
caption: ScimClient.model_name.human(count: 2),
enterprise_feature: "scim_api"
-3
View File
@@ -43,9 +43,6 @@ Rails.application.config.to_prepare do
)
return handle_scim_error(error)
end
if !OpenProject::FeatureDecisions.scim_api_active?
return handle_scim_error(Scimitar::AuthenticationError.new)
end
warden = request.env["warden"]
user = warden.authenticate(scope: :scim_v2)
+2 -18
View File
@@ -38,7 +38,7 @@ RSpec.describe "SCIM API Authentication" do
let(:scim_client) { create(:scim_client, authentication_method: :oauth2_token, auth_provider_id: oidc_provider.id) }
describe "GET /scim_v2/ServiceProviderConfig" do
context "with the feature flag and enterprise enabled", with_ee: [:scim_api], with_flag: { scim_api: true } do
context "with enterprise feature enabled", with_ee: [:scim_api] do
context "with static token" do
let(:oauth_access_token) { create(:oauth_access_token, resource_owner: service_account, scopes: ["scim_v2"]) }
let!(:token) { oauth_access_token.plaintext_token }
@@ -157,23 +157,7 @@ RSpec.describe "SCIM API Authentication" do
end
end
context "with the feature flag disabled", with_ee: [:scim_api], with_flag: { scim_api: false } do
let(:oauth_access_token) { create(:oauth_access_token, resource_owner: service_account, scopes: ["scim_v2"]) }
let!(:token) { oauth_access_token.plaintext_token }
it do
get "/scim_v2/ServiceProviderConfig", {}, headers
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" }
)
expect(last_response).to have_http_status(401)
end
end
context "with the enterprise feature missing", with_flag: { scim_api: true } do
context "with the enterprise feature missing" do
let(:oauth_access_token) { create(:oauth_access_token, resource_owner: service_account, scopes: ["scim_v2"]) }
let!(:token) { oauth_access_token.plaintext_token }
+357 -452
View File
@@ -54,507 +54,412 @@ RSpec.describe "SCIM API Groups", with_ee: [:scim_api] do
end
describe "GET /scim_v2/Groups" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
before { group }
before { group }
it "responds with a list of groups excluding marked for deletion" do
create(:group_marked_for_deletion)
it "responds with a list of groups excluding marked for deletion" do
create(:group_marked_for_deletion)
get "/scim_v2/Groups", {}, headers
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)
end
it "filters results" do
filter = ERB::Util.url_encode("displayName Eq \"#{group.name}\"")
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)
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)
end
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)
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
get "/scim_v2/Groups", {}, headers
it "filters results" do
filter = ERB::Util.url_encode("displayName Eq \"#{group.name}\"")
get "/scim_v2/Groups?filter=#{filter}", {}, headers
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"
)
expect(last_response).to have_http_status(401)
end
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)
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)
end
end
describe "GET /scim_v2/Groups/:id" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
it "responds with specific group data" do
group
get "/scim_v2/Groups/#{group.id}", {}, headers
it "responds with specific group data" do
group
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"])
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["members"]).to be_nil
end
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"])
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
get "/scim_v2/Groups/#{group.id}", {}, headers
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(
"detail" => "Requires authentication",
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status" => "401"
)
expect(last_response).to have_http_status(401)
end
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["members"]).to be_nil
end
end
describe "POST /scim_v2/Groups/" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
let(:group_name) { "Group 123" }
let(:group_name) { "Group 123" }
it "creates a group with members" do
user
request_body = { "displayName" => group_name,
"externalId" => external_group_id,
"members" => [{ "value" => user.id.to_s,
"$ref" => "/Users/#{user.id}" }],
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] }
expect do
post "/scim_v2/Groups/", request_body.to_json, headers
end.to change(Group, :count).by(1)
it "creates a group with members" do
user
request_body = { "displayName" => group_name,
"externalId" => external_group_id,
"members" => [{ "value" => user.id.to_s,
"$ref" => "/Users/#{user.id}" }],
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] }
expect do
post "/scim_v2/Groups/", request_body.to_json, headers
end.to change(Group, :count).by(1)
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"])
end
it "creates group without members specified" do
user
request_body = { "displayName" => "Group 123",
"externalId" => external_group_id,
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] }
expect do
post "/scim_v2/Groups/", request_body.to_json, headers
end.to change(Group, :count).by(1)
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"])
end
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"])
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
post "/scim_v2/Groups/", "", headers
it "creates group without members specified" do
user
request_body = { "displayName" => "Group 123",
"externalId" => external_group_id,
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"] }
expect do
post "/scim_v2/Groups/", request_body.to_json, headers
end.to change(Group, :count).by(1)
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"
)
expect(last_response).to have_http_status(401)
end
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"])
end
end
describe "DELETE /scim_v2/Groups/:id" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
it "deletes specific group" do
group
delete "/scim_v2/Groups/#{group.id}", "", headers
it "deletes specific group" do
group
delete "/scim_v2/Groups/#{group.id}", "", headers
expect(last_response.body).to eq("")
expect(last_response).to have_http_status(204)
expect(last_response.body).to eq("")
expect(last_response).to have_http_status(204)
get "/scim_v2/Groups/#{group.id}", "", headers
get "/scim_v2/Groups/#{group.id}", "", headers
expect(last_response).to have_http_status(404)
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"
)
expect(last_response).to have_http_status(404)
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"
)
perform_enqueued_jobs
assert_performed_jobs 1
perform_enqueued_jobs
assert_performed_jobs 1
get "/scim_v2/Groups/#{group.id}", "", headers
get "/scim_v2/Groups/#{group.id}", "", headers
expect(last_response).to have_http_status(404)
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"
)
end
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
delete "/scim_v2/Users/123", "", headers
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"
)
expect(last_response).to have_http_status(401)
end
expect(last_response).to have_http_status(404)
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"
)
end
end
describe "PUT /scim_v2/Groups/:id" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
let(:new_external_group_id) { "new_idp_group_id_123asdqwe12345" }
let(:new_external_group_id) { "new_idp_group_id_123asdqwe12345" }
before do
admin
group
end
it "updates specific group by replacing it with newly provided data" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"active" => true,
"externalId" => new_external_group_id,
"displayName" => group.name,
"members" => [
{ "value" => user.id.to_s },
{ "value" => admin.id.to_s }
]
}
put "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "updates members if there is $ref field present for every member(Keycloak plugin adds it for example)" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"externalId" => new_external_group_id,
"displayName" => group.name,
"members" => [
{
"value" => user.id.to_s,
"$ref" => "/Users/#{user.id}"
},
{
"value" => admin.id.to_s,
"$ref" => "/Users/#{admin.id}"
}
]
}
put "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "updates members if there is no members field(Keycloak plugin sends memberless group request like that)" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"externalId" => new_external_group_id,
"displayName" => group.name
}
put "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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" => [],
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
before do
admin
group
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
put "/scim_v2/Groups/123", "", headers
it "updates specific group by replacing it with newly provided data" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"active" => true,
"externalId" => new_external_group_id,
"displayName" => group.name,
"members" => [
{ "value" => user.id.to_s },
{ "value" => admin.id.to_s }
]
}
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"
)
expect(last_response).to have_http_status(401)
end
put "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "updates members if there is $ref field present for every member(Keycloak plugin adds it for example)" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"externalId" => new_external_group_id,
"displayName" => group.name,
"members" => [
{
"value" => user.id.to_s,
"$ref" => "/Users/#{user.id}"
},
{
"value" => admin.id.to_s,
"$ref" => "/Users/#{admin.id}"
}
]
}
put "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "updates members if there is no members field(Keycloak plugin sends memberless group request like that)" do
request_body = {
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"externalId" => new_external_group_id,
"displayName" => group.name
}
put "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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" => [],
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:Group"])
end
end
describe "PATCH /scim_v2/Groups/:id" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
it "supports external_id replacing" do
group
new_external_group_id = "new_idp_user_id_123asdqwe12345"
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "externalId",
"value" => new_external_group_id
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
it "supports external_id replacing" do
group
new_external_group_id = "new_idp_user_id_123asdqwe12345"
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "externalId",
"value" => new_external_group_id
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports replacing of members" do
group
user2
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "members",
"value" => [{
"value" => user2.id.to_s,
"$ref" => "/Users/#{user2.id}"
}]
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports adding of a member" do
group
user2
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "add",
"path" => "members",
"value" => [{ "value" => user2.id.to_s }]
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports removal of a member" do
group
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "remove",
"path" => "members",
"value" => [{ "value" => user1.id.to_s }]
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports removal of a member with exclusion of members list from the response" do
group
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "remove",
"path" => "members",
"value" => [{ "value" => user1.id.to_s }]
}]
}
patch "/scim_v2/Groups/#{group.id}?excludedAttributes=members", request_body.to_json, headers
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"])
end
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"])
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
patch "/scim_v2/Groups/123", "", headers
it "supports replacing of members" do
group
user2
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"
)
end
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "members",
"value" => [{
"value" => user2.id.to_s,
"$ref" => "/Users/#{user2.id}"
}]
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports adding of a member" do
group
user2
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "add",
"path" => "members",
"value" => [{ "value" => user2.id.to_s }]
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports removal of a member" do
group
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "remove",
"path" => "members",
"value" => [{ "value" => user1.id.to_s }]
}]
}
patch "/scim_v2/Groups/#{group.id}", request_body.to_json, headers
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"])
end
it "supports removal of a member with exclusion of members list from the response" do
group
request_body = {
"schemas" =>
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "remove",
"path" => "members",
"value" => [{ "value" => user1.id.to_s }]
}]
}
patch "/scim_v2/Groups/#{group.id}?excludedAttributes=members", request_body.to_json, headers
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"])
end
end
end
+217 -232
View File
@@ -41,240 +41,225 @@ RSpec.describe "SCIM API Schemas", with_ee: [:scim_api] do
before { token }
describe "GET /scim_v2/Schemas" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
it "responds with supported schemas" do
get "/scim_v2/Schemas", {}, headers
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"])
group_schema = response_body["Resources"].find { |r| r["name"] == "Group" }
user_schema = response_body["Resources"].find { |r| r["name"] == "User" }
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"])
group_schema = response_body["Resources"].find { |r| r["name"] == "Group" }
user_schema = response_body["Resources"].find { |r| r["name"] == "User" }
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" => false,
"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" => "server",
"returned" => "default",
"name" => "externalId",
"type" => "string" },
{ "multiValued" => false,
"required" => false,
"caseExact" => true,
"mutability" => "readOnly",
"uniqueness" => "server",
"returned" => "default",
"name" => "id",
"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" => false,
"mutability" => "readWrite",
"uniqueness" => "server",
"returned" => "default",
"name" => "userName",
"type" => "string" },
{ "multiValued" => false,
"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" => "familyName",
"type" => "string" },
{ "multiValued" => false,
"required" => true,
"caseExact" => false,
"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" => "server",
"returned" => "default",
"name" => "externalId",
"type" => "string" },
{ "multiValued" => false,
"required" => false,
"caseExact" => true,
"mutability" => "readOnly",
"uniqueness" => "server",
"returned" => "default",
"name" => "id",
"type" => "string" }
]
)
end
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
get "/scim_v2/Schemas", {}, headers
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"
)
end
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" => false,
"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" => "server",
"returned" => "default",
"name" => "externalId",
"type" => "string" },
{ "multiValued" => false,
"required" => false,
"caseExact" => true,
"mutability" => "readOnly",
"uniqueness" => "server",
"returned" => "default",
"name" => "id",
"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" => false,
"mutability" => "readWrite",
"uniqueness" => "server",
"returned" => "default",
"name" => "userName",
"type" => "string" },
{ "multiValued" => false,
"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" => "familyName",
"type" => "string" },
{ "multiValued" => false,
"required" => true,
"caseExact" => false,
"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" => "server",
"returned" => "default",
"name" => "externalId",
"type" => "string" },
{ "multiValued" => false,
"required" => false,
"caseExact" => true,
"mutability" => "readOnly",
"uniqueness" => "server",
"returned" => "default",
"name" => "id",
"type" => "string" }
]
)
end
end
end
@@ -42,64 +42,48 @@ RSpec.describe "SCIM API ServiceProviderConfig" do
describe "GET /scim_v2/ServiceProviderConfig" do
context "with enterprise token supporting scim_api", with_ee: [:scim_api] do
context "with the feature flag enabled", with_flag: { scim_api: true } do
it "responds with full ServiceProviderConfig information if authorization is correct" do
it "responds with full ServiceProviderConfig information if authorization is correct" do
get "/scim_v2/ServiceProviderConfig", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to include("authenticationSchemes" => [{ "description" => "Bearer Token can be obtained in 3 different ways(https://www.openproject.org/docs/system-admin-guide/authentication/scim/#step-3-choose-an-authentication-method)",
"name" => "OAuth Bearer Token",
"type" => "oauthbearertoken" }],
"bulk" => { "supported" => false },
"changePassword" => { "supported" => false },
"etag" => { "supported" => false },
"filter" => { "maxResults" => 100,
"supported" => true },
"patch" => { "supported" => true },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
"sort" => { "supported" => false })
end
context "when authorization header contains an invalid token" do
let(:token) { object_double(Doorkeeper::AccessToken.new, plaintext_token: "123123") }
it "responds with 401 Unauthorized" do
get "/scim_v2/ServiceProviderConfig", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to include("authenticationSchemes" => [{ "description" => "Bearer Token can be obtained in 3 different ways(https://www.openproject.org/docs/system-admin-guide/authentication/scim/#step-3-choose-an-authentication-method)",
"name" => "OAuth Bearer Token",
"type" => "oauthbearertoken" }],
"bulk" => { "supported" => false },
"changePassword" => { "supported" => false },
"etag" => { "supported" => false },
"filter" => { "maxResults" => 100,
"supported" => true },
"patch" => { "supported" => true },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
"sort" => { "supported" => false })
end
context "when authorization header contains an invalid token" do
let(:token) { object_double(Doorkeeper::AccessToken.new, plaintext_token: "123123") }
it "responds with 401 Unauthorized" do
get "/scim_v2/ServiceProviderConfig", {}, headers
expect(last_response).to have_http_status(401)
expect(last_response.body).to eq("invalid_token")
end
end
context "when there is no authorization header at all" do
let(:token) { object_double(Doorkeeper::AccessToken.new, plaintext_token: "123123") }
it "responds with limited ServiceProviderConfig information" do
get "/scim_v2/ServiceProviderConfig", {}, { "CONTENT_TYPE" => "application/scim+json" }
expect(last_response).to have_http_status(200)
response_body = JSON.parse(last_response.body)
expect(response_body.keys).to eq(["meta", "schemas", "authenticationSchemes"])
expect(response_body).to include("authenticationSchemes" => [{ "description" => "Bearer Token can be obtained in 3 different ways(https://www.openproject.org/docs/system-admin-guide/authentication/scim/#step-3-choose-an-authentication-method)",
"name" => "OAuth Bearer Token",
"type" => "oauthbearertoken" }],
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"])
end
expect(last_response).to have_http_status(401)
expect(last_response.body).to eq("invalid_token")
end
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
it do
get "/scim_v2/ServiceProviderConfig", {}, headers
context "when there is no authorization header at all" do
let(:token) { object_double(Doorkeeper::AccessToken.new, plaintext_token: "123123") }
it "responds with limited ServiceProviderConfig information" do
get "/scim_v2/ServiceProviderConfig", {}, { "CONTENT_TYPE" => "application/scim+json" }
expect(last_response).to have_http_status(200)
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" }
)
expect(last_response).to have_http_status(401)
expect(response_body.keys).to eq(["meta", "schemas", "authenticationSchemes"])
expect(response_body).to include("authenticationSchemes" => [{ "description" => "Bearer Token can be obtained in 3 different ways(https://www.openproject.org/docs/system-admin-guide/authentication/scim/#step-3-choose-an-authentication-method)",
"name" => "OAuth Bearer Token",
"type" => "oauthbearertoken" }],
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"])
end
end
end
File diff suppressed because it is too large Load Diff