[#62107] React to the PR comments.

This commit is contained in:
Pavel Balashou
2025-03-27 20:27:21 +01:00
parent be1a7b3973
commit 22efc29371
7 changed files with 220 additions and 93 deletions
+1 -1
View File
@@ -1506,7 +1506,7 @@ DEPENDENCIES
ruby-progressbar (~> 1.13.0)
rubytree (~> 2.1.0)
sanitize (~> 7.0.0)
scimitar
scimitar (~> 2.11)
secure_headers (~> 7.1.0)
selenium-devtools
selenium-webdriver (~> 4.20)
@@ -0,0 +1,71 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module ScimV2
module BaseControllerActions
extend ActiveSupport::Concern
included do
skip_before_action :verify_authenticity_token
rescue_from "ActiveRecord::RecordNotFound", with: :handle_resource_not_found
def index
query = if params[:filter].blank?
storage_scope
else
attribute_map = storage_class.new.scim_queryable_attributes
parser = ::Scimitar::Lists::QueryParser.new(attribute_map)
parser.parse(params[:filter])
parser.to_activerecord_query(storage_scope)
end
pagination_info = scim_pagination_info(query.count)
page_of_results = query
.order(id: :asc)
.offset(pagination_info.offset)
.limit(pagination_info.limit)
.to_a
super(pagination_info, page_of_results) do |record|
record.to_scim(location: url_for(action: :show, id: record.id))
end
end
def show
super do |record_id|
record = storage_scope.find(record_id)
record.to_scim(location: url_for(action: :show, id: record_id))
end
end
end
end
end
+29 -33
View File
@@ -1,40 +1,36 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module ScimV2
class GroupsController < Scimitar::ResourcesController
skip_before_action :verify_authenticity_token
rescue_from "ActiveRecord::RecordNotFound", with: :handle_resource_not_found
def index
query = if params[:filter].blank?
storage_scope
else
attribute_map = storage_class.new.scim_queryable_attributes
parser = ::Scimitar::Lists::QueryParser.new(attribute_map)
parser.parse(params[:filter])
parser.to_activerecord_query(storage_scope)
end
pagination_info = scim_pagination_info(query.count)
page_of_results = query
.order(id: :asc)
.offset(pagination_info.offset)
.limit(pagination_info.limit)
.to_a
super(pagination_info, page_of_results) do |record|
record.to_scim(location: url_for(action: :show, id: record.id))
end
end
def show
super do |group_id|
group = storage_scope.find(group_id)
group.to_scim(location: url_for(action: :show, id: group_id))
end
end
include BaseControllerActions
def create
super do |scim_resource|
+30 -34
View File
@@ -1,40 +1,36 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module ScimV2
class UsersController < Scimitar::ResourcesController
skip_before_action :verify_authenticity_token
rescue_from "ActiveRecord::RecordNotFound", with: :handle_resource_not_found
def index
query = if params[:filter].blank?
storage_scope
else
attribute_map = storage_class.new.scim_queryable_attributes
parser = ::Scimitar::Lists::QueryParser.new(attribute_map)
parser.parse(params[:filter])
parser.to_activerecord_query(storage_scope)
end
pagination_info = scim_pagination_info(query.count)
page_of_results = query
.order(id: :asc)
.offset(pagination_info.offset)
.limit(pagination_info.limit)
.to_a
super(pagination_info, page_of_results) do |record|
record.to_scim(location: url_for(action: :show, id: record.id))
end
end
def show
super do |user_id|
user = storage_scope.find(user_id)
user.to_scim(location: url_for(action: :show, id: user_id))
end
end
include BaseControllerActions
def create
super do |scim_resource|
@@ -97,7 +93,7 @@ module ScimV2
end
def storage_scope
User.user.where.not(identity_url: [nil, ""])
User.not_builtin
end
end
end
+2 -2
View File
@@ -111,7 +111,7 @@ class Group < Principal
case type.downcase
when "user"
User.user.where.not(identity_url: [nil, ""]).find_by(id:)
User.not_builtin.find_by(id:)
when "group"
# TODO OP does not support nesting of groups but SCIM does.
# For now raises exception in case of group as a member arrival.
@@ -130,7 +130,7 @@ class Group < Principal
def self.scim_queryable_attributes
{
displayName: :name
displayName: { column: :lastname }
}
end
+29 -19
View File
@@ -62,25 +62,35 @@ RSpec.describe "SCIM API Groups" do
"totalResults" => 1 })
end
# it do
# filter = ERB::Util.url_encode('displayName Eq "john"')
# get "/scim_v2/Groups?filter=#{filter}", {}, 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({ "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 })
# 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
context "with the feature flag disabled", with_flag: { scim_api: false } do
@@ -265,7 +275,7 @@ RSpec.describe "SCIM API Groups" do
new_external_group_id = "new_idp_user_id_123asdqwe12345"
request_body = {
"schemas" =>
["urn =>ietf:params:scim:api:messages:2.0:PatchOp"],
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "externalId",
+58 -4
View File
@@ -41,11 +41,54 @@ RSpec.describe "SCIM API Users" do
describe "GET /scim_v2/Users" do
context "with the feature flag enabled", with_flag: { scim_api: true } do
it do
group
before { group }
it do
get "/scim_v2/Users", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq("Resources" =>
[{ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => admin.mail }],
"externalId" => nil,
"groups" => [],
"id" => admin.id.to_s,
"meta" => { "created" => admin.created_at.iso8601,
"lastModified" => admin.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{admin.id}",
"resourceType" => "User" },
"name" => { "familyName" => admin.lastname,
"givenName" => admin.firstname },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => admin.login },
{ "active" => true,
"emails" => [{ "primary" => true,
"type" => "work",
"value" => user.mail }],
"externalId" => external_user_id,
"groups" => [{ "value" => group.id.to_s }],
"id" => user.id.to_s,
"meta" => { "created" => user.created_at.iso8601,
"lastModified" => user.updated_at.iso8601,
"location" => "http://test.host/scim_v2/Users/#{user.id}",
"resourceType" => "User" },
"name" => { "familyName" => user.lastname,
"givenName" => user.firstname },
"schemas" => ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName" => user.login },
],
"itemsPerPage" => 100,
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 2)
end
it "filters results" do
filter_with_existing_rows = ERB::Util.url_encode('familyName Eq "' + user.lastname + '"')
get "/scim_v2/Users?filter=#{filter_with_existing_rows}", {}, headers
response_body = JSON.parse(last_response.body)
expect(response_body).to eq("Resources" => [{ "active" => true,
"emails" => [{ "primary" => true,
@@ -66,7 +109,18 @@ RSpec.describe "SCIM API Users" do
"schemas" => ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"startIndex" => 1,
"totalResults" => 1)
filter_with_nonexisting_rows = ERB::Util.url_encode('familyName Eq "NONEXISTENT USER LASTNAME"')
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})
end
end
context "with the feature flag disabled", with_flag: { scim_api: false } do
@@ -303,7 +357,7 @@ RSpec.describe "SCIM API Users" do
it "changes external_id" do
request_body = {
"schemas" =>
["urn =>ietf:params:scim:api:messages:2.0:PatchOp"],
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "externalId",
@@ -335,7 +389,7 @@ RSpec.describe "SCIM API Users" do
new_email_value = "qwertty@gmail.com"
request_body = {
"schemas" =>
["urn =>ietf:params:scim:api:messages:2.0:PatchOp"],
["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations" => [{
"op" => "replace",
"path" => "emails[type eq \"work\"]",