Skip to content

Commit

Permalink
Merge pull request #13479 from opf/bug/49623-access-to-project-folder…
Browse files Browse the repository at this point in the history
…s-for-non-members-and-admins

[#49623] Access to project folders for non members and admins.
  • Loading branch information
ba1ash committed Aug 17, 2023
2 parents 4990387 + 39dc644 commit 7357206
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Storages::GroupFolderPropertiesSyncService
delete_files: 8,
share_files: 16
}.freeze
PERMISSIONS_KEYS = PERMISSIONS_MAP.keys.freeze
ALL_PERMISSIONS = PERMISSIONS_MAP.values.sum
NO_PERMISSIONS = 0

Expand All @@ -50,11 +51,15 @@ def initialize(storage)
# rubocop:disable Metrics/AbcSize
def call
# we have to flush state to be able to reuse the instace of the class
@nextcloud_usernames_used_in_openproject = Set.new
@project_folder_ids_used_in_openproject = Set.new
@file_ids = nil
@folders_properties = nil
@group_users = nil
@admin_tokens_query = OAuthClientToken.where(oauth_client: @storage.oauth_client,
users: User.admin.active)

@admin_nextcloud_usernames = @admin_tokens_query.pluck(:origin_user_id)
@nextcloud_usernames_used_in_openproject = @admin_nextcloud_usernames.to_set

set_group_folder_root_permissions

Expand Down Expand Up @@ -104,7 +109,7 @@ def ensure_project_folder(project_storage:, project_folder_path:)
else
create_folder(path: project_folder_path, project_storage:) >> obtain_file_id >> save_file_id
end
# local variable is not used due to possible update_columns call
# local variable `project_folder_id` is not used due to possible update_columns call
# then the value inside the local variable will not be updated
project_storage.project_folder_id
end
Expand Down Expand Up @@ -196,19 +201,37 @@ def group_users
end

def project_folder_permissions(project:)
OAuthClientToken
.where(oauth_client: @storage.oauth_client, users: project.users)
.includes(:user)
.each_with_object({
users: { "#{@nextcloud_system_user}": ALL_PERMISSIONS },
groups: { "#{@group}": NO_PERMISSIONS }
}) do |token, permissions|
tokens_query = OAuthClientToken
.where(oauth_client: @storage.oauth_client)
.where.not(id: @admin_tokens_query)
.includes(:user)
# The user scope is required in all cases except one:
# when the project is public and non member has at least one storage permission
# then all non memebers should have access to the project folder
if !(project.public? && Role.non_member.permissions.intersect?(PERMISSIONS_KEYS))
tokens_query = tokens_query.where(users: project.users)
end
tokens_query.each_with_object({
users: admins_project_folder_permissions,
groups: { "#{@group}": NO_PERMISSIONS }
}) do |token, permissions|
nextcloud_username = token.origin_user_id
permissions[:users][nextcloud_username.to_sym] = calculate_permissions(user: token.user, project:)
@nextcloud_usernames_used_in_openproject << nextcloud_username
end
end

def admins_project_folder_permissions
@admins_project_folder_permissions ||=
{
"#{@nextcloud_system_user}": ALL_PERMISSIONS
}.tap do |map|
@admin_nextcloud_usernames.each do |admin_nextcloud_username|
map[admin_nextcloud_username.to_sym] = ALL_PERMISSIONS
end
end
end

def add_active_users_to_group
@nextcloud_usernames_used_in_openproject.each do |nextcloud_username|
if group_users.exclude?(nextcloud_username)
Expand Down
2 changes: 1 addition & 1 deletion modules/storages/lib/open_project/storages/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
module OpenProject::Storages
class Engine < ::Rails::Engine
def self.permissions
@permissions ||= Storages::GroupFolderPropertiesSyncService::PERMISSIONS_MAP.keys
@permissions ||= Storages::GroupFolderPropertiesSyncService::PERMISSIONS_KEYS
end
# engine name is used as a default prefix for module tables when generating
# tables with the rails command.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
<d:response>
<d:href>/remote.php/dav/files/OpenProject/OpenProject/PUBLIC%20PROJECT%20%28#{public_project.id}%29/</d:href>
<d:propstat>
<d:prop>
<oc:fileid>999</oc:fileid>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
XML
end
Expand Down Expand Up @@ -207,6 +216,12 @@
<nc:acl-mask>31</nc:acl-mask>
<nc:acl-permissions>31</nc:acl-permissions>
</nc:acl>
<nc:acl>
<nc:acl-mapping-type>user</nc:acl-mapping-type>
<nc:acl-mapping-id>Darth Vader</nc:acl-mapping-id>
<nc:acl-mask>31</nc:acl-mask>
<nc:acl-permissions>31</nc:acl-permissions>
</nc:acl>
<nc:acl>
<nc:acl-mapping-type>user</nc:acl-mapping-type>
<nc:acl-mapping-id>Obi-Wan</nc:acl-mapping-id>
Expand Down Expand Up @@ -364,13 +379,73 @@
</d:multistatus>
XML
end
let(:set_permissions_request_body6) do
<<~XML
<?xml version="1.0"?>
<d:propertyupdate xmlns:d="DAV:" xmlns:nc="http://nextcloud.org/ns">
<d:set>
<d:prop>
<nc:acl-list>
<nc:acl>
<nc:acl-mapping-type>group</nc:acl-mapping-type>
<nc:acl-mapping-id>OpenProject</nc:acl-mapping-id>
<nc:acl-mask>31</nc:acl-mask>
<nc:acl-permissions>0</nc:acl-permissions>
</nc:acl>
<nc:acl>
<nc:acl-mapping-type>user</nc:acl-mapping-type>
<nc:acl-mapping-id>OpenProject</nc:acl-mapping-id>
<nc:acl-mask>31</nc:acl-mask>
<nc:acl-permissions>31</nc:acl-permissions>
</nc:acl>
<nc:acl>
<nc:acl-mapping-type>user</nc:acl-mapping-type>
<nc:acl-mapping-id>Darth Vader</nc:acl-mapping-id>
<nc:acl-mask>31</nc:acl-mask>
<nc:acl-permissions>31</nc:acl-permissions>
</nc:acl>
<nc:acl>
<nc:acl-mapping-type>user</nc:acl-mapping-type>
<nc:acl-mapping-id>Obi-Wan</nc:acl-mapping-id>
<nc:acl-mask>31</nc:acl-mask>
<nc:acl-permissions>1</nc:acl-permissions>
</nc:acl>
</nc:acl-list>
</d:prop>
</d:set>
</d:propertyupdate>
XML
end
let(:set_permissions_response_body6) do
<<~XML
<?xml version="1.0"?>
<d:multistatus
xmlns:d="DAV:"
xmlns:s="http://sabredav.org/ns"
xmlns:oc="http://owncloud.org/ns"
xmlns:nc="http://nextcloud.org/ns">
<d:response>
<d:href>/remote.php/dav/files/OpenProject/OpenProject/PUBLIC%20PROJECT%20%28#{public_project.id}%29/</d:href>
<d:propstat>
<d:prop>
<nc:acl-list/>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
XML
end
let(:request_stubs) { [] }

let(:project1) { create(:project, name: '[Sample] Project Name / Ehuu', members: { user => role }) }
let(:project2) { create(:project, name: 'Jedi Project Folder ///', members: { user => role }) }
let(:project3) { create(:project, name: 'NOT ACTIVE PROJECT', active: false, members: { user => role }) }
let(:project1) { create(:project, name: '[Sample] Project Name / Ehuu', members: { user => ordinary_role }) }
let(:project2) { create(:project, name: 'Jedi Project Folder ///', members: { user => ordinary_role }) }
let(:project3) { create(:project, name: 'NOT ACTIVE PROJECT', active: false, members: { user => ordinary_role }) }
let(:public_project) { create(:public_project, name: 'PUBLIC PROJECT', active: true) }
let(:user) { create(:user) }
let(:role) { create(:role, permissions: %w[read_files write_files]) }
let!(:admin) { create(:admin) }
let(:ordinary_role) { create(:role, permissions: %w[read_files write_files]) }
let!(:non_member_role) { create(:non_member, permissions: %w[read_files]) }
let(:storage) do
create(:nextcloud_storage,
:as_automatically_managed,
Expand All @@ -397,6 +472,13 @@
storage:,
project_folder_id: '778')
end
let!(:project_storage4) do
create(:project_storage,
project_folder_mode: 'automatic',
project: public_project,
storage:,
project_folder_id: '999')
end

let(:oauth_client) { create(:oauth_client, integration: storage) }
# rubocop:enable RSpec/IndexedLet
Expand All @@ -406,6 +488,10 @@
origin_user_id: 'Obi-Wan',
user:,
oauth_client:)
create(:oauth_client_token,
origin_user_id: 'Darth Vader',
user: admin,
oauth_client:)
request_stubs << stub_request(:get, "#{storage.host}/ocs/v1.php/cloud/groups/#{storage.group}")
.with(
headers: {
Expand Down Expand Up @@ -455,6 +541,14 @@
'Ocs-Apirequest' => 'true'
}
).to_return(status: 200, body: add_user_to_group_response_body, headers: {})
request_stubs << stub_request(:post, "#{storage.host}/ocs/v1.php/cloud/users/Darth%20Vader/groups")
.with(
body: "groupid=OpenProject",
headers: {
'Authorization' => 'Basic T3BlblByb2plY3Q6MTIzNDU2Nzg=',
'Ocs-Apirequest' => 'true'
}
).to_return(status: 200, body: add_user_to_group_response_body, headers: {})
request_stubs << stub_request(
:proppatch,
"#{storage.host}/remote.php/dav/files/OpenProject/OpenProject/" \
Expand Down Expand Up @@ -486,6 +580,16 @@
'Authorization' => 'Basic T3BlblByb2plY3Q6MTIzNDU2Nzg='
}
).to_return(status: 207, body: set_permissions_response_body4, headers: {})
request_stubs << stub_request(
:proppatch,
"#{storage.host}/remote.php/dav/files/OpenProject/OpenProject/" \
"PUBLIC%20PROJECT%20%28#{public_project.id}%29"
).with(
body: set_permissions_request_body6,
headers: {
'Authorization' => 'Basic T3BlblByb2plY3Q6MTIzNDU2Nzg='
}
).to_return(status: 207, body: set_permissions_response_body6, headers: {})
request_stubs << stub_request(
:proppatch,
"#{storage.host}/remote.php/dav/files/OpenProject/OpenProject/" \
Expand Down

0 comments on commit 7357206

Please sign in to comment.