mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
[#62107] React to the PR comments.
This commit is contained in:
+1
-1
@@ -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
|
||||
@@ -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|
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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\"]",
|
||||
|
||||
Reference in New Issue
Block a user