diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/internal/propfind_query_legacy.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/internal/propfind_query_legacy.rb
deleted file mode 100644
index 4214f3114773..000000000000
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/internal/propfind_query_legacy.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-# 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 Storages
- module Peripherals
- module StorageInteraction
- module Nextcloud
- module Internal
- class PropfindQueryLegacy
- include TaggedLogging
- def self.call(storage:, depth:, path:, props:)
- new(storage).call(depth:, path:, props:)
- end
-
- def initialize(storage)
- @storage = storage
- @username = storage.username
- @password = storage.password
- @group = storage.group
- end
-
- # rubocop:disable Metrics/AbcSize
- def call(depth:, path:, props:)
- with_tagged_logger do
- body = Nokogiri::XML::Builder.new do |xml|
- xml["d"].propfind(
- "xmlns:d" => "DAV:",
- "xmlns:oc" => "http://owncloud.org/ns",
- "xmlns:nc" => "http://nextcloud.org/ns"
- ) do
- xml["d"].prop do
- props.each do |prop|
- namespace, property = prop.split(":")
- xml[namespace].public_send(property)
- end
- end
- end
- end.to_xml
-
- response = OpenProject
- .httpx
- .basic_auth(@username, @password)
- .with(headers: { "Depth" => depth })
- .request(
- "PROPFIND",
- UrlBuilder.url(@storage.uri, "remote.php/dav/files", @username, path),
- xml: body
- )
-
- error_data = StorageErrorData.new(source: self.class, payload: response)
-
- case response
- in { status: 200..299 }
- log_response(response)
- info "Parsing XML response body"
- doc = Nokogiri::XML(response.body.to_s)
- info "Parsing response body"
- result = doc.xpath("/d:multistatus/d:response").each_with_object({}) do |resource_section, hash|
- source_path = UrlBuilder.path(@storage.uri.path, "/remote.php/dav/files", @username)
- resource = CGI.unescape(resource_section.xpath("d:href").text.strip).gsub!(source_path, "")
-
- hash[resource] = {}
-
- # In future it could be useful to respond not only with found, but not found props as well
- # resource_section.xpath("d:propstat[d:status[text() = 'HTTP/1.1 404 Not Found']]/d:prop/*")
- resource_section.xpath("d:propstat[d:status[text() = 'HTTP/1.1 200 OK']]/d:prop/*").each do |node|
- hash[resource][node.name.to_s] = node.text.strip
- end
- end
-
- info "Response parsed found: #{result.inspect}"
- ServiceResult.success(result:)
- in { status: 405 }
- log_response(response)
- Util.error(:not_allowed, "Outbound request method not allowed", error_data)
- in { status: 401 }
- log_response(response)
- Util.error(:unauthorized, "Outbound request not authorized", error_data)
- in { status: 404 }
- log_response(response)
- Util.error(:not_found, "Outbound request destination not found", error_data)
- else
- Util.error(:error, "Outbound request failed", error_data)
- end
- end
- end
- # rubocop:enable Metrics/AbcSize
-
- def log_response(response)
- info "Storage responded with a #{response.status} code."
- end
- end
- end
- end
- end
- end
-end
diff --git a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/remove_user_from_group_command.rb b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/remove_user_from_group_command.rb
index 4d15239c1b06..d25ddcff0e91 100644
--- a/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/remove_user_from_group_command.rb
+++ b/modules/storages/app/common/storages/peripherals/storage_interaction/nextcloud/remove_user_from_group_command.rb
@@ -34,61 +34,73 @@ module StorageInteraction
module Nextcloud
class RemoveUserFromGroupCommand
include TaggedLogging
- def self.call(storage:, user:, group: storage.group)
- new(storage).call(user:, group:)
+
+ def self.call(storage:, auth_strategy:, user:, group:)
+ new(storage).call(auth_strategy:, user:, group:)
end
def initialize(storage)
@storage = storage
- @username = storage.username
- @password = storage.password
- @group = storage.group
end
- # rubocop:disable Metrics/AbcSize
- def call(user:, group: @group)
+ def call(auth_strategy:, user:, group:)
with_tagged_logger do
- url = UrlBuilder.url(@storage.uri, "ocs/v1.php/cloud/users", user, "groups") +
- "?groupid=#{CGI.escapeURIComponent(group)}"
+ Authentication[auth_strategy].call(storage: @storage, http_options:) do |http|
+ url = UrlBuilder.url(@storage.uri, "ocs/v1.php/cloud/users", user, "groups")
+ url += "?groupid=#{CGI.escapeURIComponent(group)}"
- info "Removing #{user} from #{group} through #{url}"
+ info "Removing #{user} from #{group} through #{url}"
- response = OpenProject.httpx.basic_auth(@username, @password)
- .with(headers: { "OCS-APIRequest" => "true" })
- .delete(url)
+ handle_response(http.delete(url))
+ end
+ end
+ end
- error_data = StorageErrorData.new(source: self.class, payload: response)
+ private
- case response
- in { status: 200..299 }
- statuscode = Nokogiri::XML(response.body.to_s).xpath("/ocs/meta/statuscode").text
- case statuscode
- when "100"
- info "User has been removed from group"
- ServiceResult.success
- when "101"
- Util.error(:error, "No group specified", error_data)
- when "102"
- Util.error(:group_does_not_exist, "Group does not exist", error_data)
- when "103"
- Util.error(:user_does_not_exist, "User does not exist", error_data)
- when "104"
- Util.error(:insufficient_privileges, "Insufficient privileges", error_data)
- when "105"
- message = Nokogiri::XML(response.body).xpath("/ocs/meta/message").text
- Util.error(:failed_to_remove, message, error_data)
- end
- in { status: 405 }
- Util.error(:not_allowed, "Outbound request method not allowed", error_data)
- in { status: 401 }
- Util.error(:unauthorized, "Outbound request not authorized", error_data)
- in { status: 404 }
- Util.error(:not_found, "Outbound request destination not found", error_data)
- in { status: 409 }
- Util.error(:conflict, Util.error_text_from_response(response), error_data)
- else
- Util.error(:error, "Outbound request failed", error_data)
- end
+ def http_options
+ Util.ocs_api_request
+ end
+
+ def handle_response(response)
+ error_data = StorageErrorData.new(source: self.class, payload: response)
+
+ case response
+ in { status: 200..299 }
+ handle_success_response(response)
+ in { status: 405 }
+ Util.error(:not_allowed, "Outbound request method not allowed", error_data)
+ in { status: 401 }
+ Util.error(:unauthorized, "Outbound request not authorized", error_data)
+ in { status: 404 }
+ Util.error(:not_found, "Outbound request destination not found", error_data)
+ in { status: 409 }
+ Util.error(:conflict, Util.error_text_from_response(response), error_data)
+ else
+ Util.error(:error, "Outbound request failed", error_data)
+ end
+ end
+
+ # rubocop:disable Metrics/AbcSize
+ def handle_success_response(response)
+ error_data = StorageErrorData.new(source: self.class, payload: response)
+
+ statuscode = Nokogiri::XML(response.body.to_s).xpath("/ocs/meta/statuscode").text
+ case statuscode
+ when "100"
+ info "User has been removed from group"
+ ServiceResult.success
+ when "101"
+ Util.error(:error, "No group specified", error_data)
+ when "102"
+ Util.error(:group_does_not_exist, "Group does not exist", error_data)
+ when "103"
+ Util.error(:user_does_not_exist, "User does not exist", error_data)
+ when "104"
+ Util.error(:insufficient_privileges, "Insufficient privileges", error_data)
+ when "105"
+ message = Nokogiri::XML(response.body).xpath("/ocs/meta/message").text
+ Util.error(:failed_to_remove, message, error_data)
end
end
diff --git a/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb b/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb
index 617ab1aec04d..45e8798aa045 100644
--- a/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb
+++ b/modules/storages/app/services/storages/nextcloud_managed_folder_sync_service.rb
@@ -122,10 +122,12 @@ def add_users_to_remote_group(users_to_add)
end
def remove_users_from_remote_group(users_to_remove)
+ group = @storage.group
+
users_to_remove.each do |user|
- remove_user_from_group.call(storage: @storage, user:).error_and do |error|
- add_error(:remove_user_from_group, error, options: { user:, group: @storage.group, reason: error.log_message })
- log_storage_error(error, group: @storage.group, user:, reason: error.log_message)
+ remove_user_from_group.call(storage: @storage, auth_strategy:, user:, group:).error_and do |error|
+ add_error(:remove_user_from_group, error, options: { user:, group:, reason: error.log_message })
+ log_storage_error(error, group:, user:, reason: error.log_message)
end
end
end
diff --git a/modules/storages/spec/common/storages/peripherals/registry_spec.rb b/modules/storages/spec/common/storages/peripherals/registry_spec.rb
index 4f614910bb0e..bcf7ee97c0f5 100644
--- a/modules/storages/spec/common/storages/peripherals/registry_spec.rb
+++ b/modules/storages/spec/common/storages/peripherals/registry_spec.rb
@@ -147,72 +147,6 @@
end
end
- describe "#remove_user_from_group" do
- let(:expected_response) do
- {
- status: 200,
- body: expected_response_body,
- headers: {}
- }
- end
- let(:expected_response_body) do
- <<~XML
-
-
-
- ok
- 100
- OK
-
-
-
-
-
- XML
- end
-
- before do
- stub_request(:delete, "https://example.com/ocs/v1.php/cloud/users/#{origin_user_id}/groups?groupid=#{storage.group}")
- .with(
- headers: {
- "Authorization" => "Basic T3BlblByb2plY3Q6T3BlblByb2plY3RTZWN1cmVQYXNzd29yZA==",
- "OCS-APIRequest" => "true"
- }
- )
- .to_return(expected_response)
- end
-
- it "removes user from the group" do
- result = registry.resolve("nextcloud.commands.remove_user_from_group").call(storage:, user: origin_user_id)
- expect(result).to be_success
- end
-
- context "when Nextcloud reponds with 105 code in the response body" do
- let(:expected_response_body) do
- <<~XML
-
-
-
- failure
- 105
- Not viable to remove user from the last group you are SubAdmin of
-
-
-
-
-
- XML
- end
-
- it "responds with a failure and parses message from the xml response" do
- result = registry.resolve("nextcloud.commands.remove_user_from_group").call(storage:, user: origin_user_id)
- expect(result).to be_failure
- expect(result.errors.log_message)
- .to eq("Not viable to remove user from the last group you are SubAdmin of")
- end
- end
- end
-
describe "#delete_folder_command" do
let(:auth_strategy) { Storages::Peripherals::StorageInteraction::AuthenticationStrategies::BasicAuth.strategy }
diff --git a/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb b/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb
index ee1a1a10edbe..0e9c207829af 100644
--- a/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb
+++ b/modules/storages/spec/services/storages/nextcloud_managed_folder_sync_service_spec.rb
@@ -188,7 +188,11 @@ module Storages
allow(group_users).to receive(:call).with(storage:, group: storage.group).and_return(group_users_result)
# Updating the group users
allow(add_user).to receive(:call).with(storage:, user: "single_project_user").and_return(add_user_result)
- allow(remove_user).to receive(:call).with(storage:, user: "cookiemonster").and_return(remove_user_result)
+ allow(remove_user).to receive(:call).with(storage:,
+ auth_strategy:,
+ user: "cookiemonster",
+ group: storage.group)
+ .and_return(remove_user_result)
end
it "applies changes to all project storages linked to the passed storage" do