-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Lock unauthorized permissions from update
Fixes #3166 - user is permitted to update any work permissions coming from collections they manage - user is permitted to update non-manager permissions from any Collections - user is permitted to update any non-collection permissions Process used: - find all of the work's collections a user can manage - find all of the work's collections a user cannot manage - find all of the other managers of collections a user can manage - find all of the other managers of collections a user cannot manage who are not also managers in collections that the user CAN manage. This gives us the permissions the user is not authorized to update. EditPermissionsService embeds all of the above logic and uses it to display all of the work or file set's permissions in either a fixed or editable format. The new shared partial results in a slight reformat of the display of these permissions, as there were previously minor differences between the two displays. Additionally, a new partial was created for batch edit, as the above rules cannot be enforced at a batch level. Dealing with batch editing sharing issues is beyond the scope of this issue. Restrict user from changing managers which are in both authorized & unauthorized collections
- Loading branch information
Showing
14 changed files
with
414 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# frozen_string_literal: true | ||
module Hyrax | ||
class EditPermissionsService | ||
# Encapsulates the logic to determine which object permissions may be edited by a given user | ||
# - user is permitted to update any work permissions coming ONLY from collections they manage | ||
# - user is not permitted to update a work permission if it comes from a collection they do not manage, even if also from a managed collection | ||
# - user is permitted to update only non-manager permissions from any Collections | ||
# - user is permitted to update any non-collection permissions | ||
attr_reader :depositor, :unauthorized_collection_managers | ||
|
||
# @param [Object] GenericWorkForm (if called for object) or GenericWork (if called for file set) | ||
# @param [Ability] user's current_ability | ||
def initialize(object:, ability:) | ||
@object = object | ||
@ability = ability | ||
@depositor = object.depositor | ||
unauthorized = manager_permissions_to_block | ||
@unauthorized_managers = unauthorized.unauthorized_managers | ||
@unauthorized_collection_managers = unauthorized.unauthorized_collection_managers | ||
end | ||
|
||
# @param [Hash] one set of permission fields for object {:name, :access} | ||
# @return [Boolean] true if user cannot edit the given permissions | ||
def cannot_edit_permissions?(permission_hash) | ||
@unauthorized_managers.include?(permission_hash[:name]) && permission_hash[:access] == "edit" | ||
end | ||
|
||
# @param [Hash] one set of permission fields for object {:name, :access} | ||
# @return [Boolean] true if given permissions are one of fixed exclusions | ||
def excluded_permission?(permission_hash) | ||
exclude_from_display.include? permission_hash[:name].downcase | ||
end | ||
|
||
private | ||
|
||
# Fixed set of users & groups to exclude from "editable" section of display | ||
def exclude_from_display | ||
[::Ability.public_group_name, ::Ability.registered_group_name, ::Ability.admin_group_name, @depositor] | ||
end | ||
|
||
BlockedPermissions = Struct.new(:unauthorized_managers, :unauthorized_collection_managers) | ||
|
||
# find all of the other managers of collections which a user cannot manage | ||
# | ||
# Process used: | ||
# - find all of the work's collections which a user can manage | ||
# - find all of the work's collections (of a type which shares permissions) that a user cannot manage | ||
# - find all of the managers of these collections the user cannot manage | ||
# This gives us the manager permissions the user is not authorized to update. | ||
# | ||
# @return [Struct] BlockedPermissions | ||
# - unauthorized_managers [Array] ids of managers of all collections | ||
# - unauthorized_collection_managers [Array hashes] manager ids & collection_ids [{:name, :id}] | ||
def manager_permissions_to_block | ||
unauthorized_managers = [] | ||
unauthorized_collection_managers = [] | ||
if object_unauthorized_collection_ids.any? | ||
object_unauthorized_collection_ids.each do |id| | ||
Hyrax::PermissionTemplate.find_by(source_id: id).access_grants.each do |grant| | ||
if grant.access == "manage" | ||
unauthorized_managers << grant.agent_id | ||
unauthorized_collection_managers += Array.wrap({ name: grant.agent_id }.merge(id: id)) | ||
end | ||
end | ||
end | ||
end | ||
BlockedPermissions.new(unauthorized_managers, unauthorized_collection_managers) | ||
end | ||
|
||
# find all of the work's collections a user can manage | ||
# @return [Array] of collection ids | ||
def object_managed_collection_ids | ||
@object_managed_collection_ids ||= object_member_of & managed_collection_ids | ||
end | ||
|
||
# find all of the work's collections a user cannot manage | ||
# note: if the collection type doesn't include "sharing_applies_to_new_works", we don't limit access | ||
# @return [Array] of collection ids with limited access | ||
def object_unauthorized_collection_ids | ||
@object_unauthorized_collection_ids ||= begin | ||
limited_access = [] | ||
unauthorized_collection_ids = object_member_of - object_managed_collection_ids | ||
if unauthorized_collection_ids.any? | ||
unauthorized_collection_ids.each do |id| | ||
collection = ActiveFedora::Base.find(id) | ||
limited_access << id if (collection.instance_of? AdminSet) || collection.share_applies_to_new_works? | ||
end | ||
end | ||
limited_access | ||
end | ||
end | ||
|
||
# find all of the collection ids an object is a member of | ||
# @return [Array] array of collection ids | ||
def object_member_of | ||
@object_member_of ||= begin | ||
belongs_to = [] | ||
# get all of work's collection ids from the form | ||
@object.member_of_collections.each do |collection| | ||
belongs_to << collection.id | ||
end | ||
belongs_to << @object.admin_set_id unless @object.admin_set_id.empty? | ||
belongs_to | ||
end | ||
end | ||
|
||
# The list of all collections this user has manage rights on | ||
# @return [Array] array of all collection ids that user can manage | ||
def managed_collection_ids | ||
Hyrax::Collections::PermissionsService.source_ids_for_manage(ability: @ability) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<%# | ||
# form object.class = SimpleForm::FormBuilder | ||
# For works (i.e. GenericWork): | ||
# - form object.object = Hyrax::GenericWorkForm | ||
# - form object.object.model = GenericWork | ||
# - use the work itself | ||
# For file_sets: | ||
# - form object.object.class = FileSet | ||
# - use work the file_set is in | ||
# No other object types are supported by this view. %> | ||
<% if f.object.respond_to?(:model) && f.object.model.work? | ||
object_acting_upon = f.object | ||
elsif f.object.file_set? | ||
object_acting_upon = f.object.in_works.first | ||
end %> | ||
<% permission_service = Hyrax::EditPermissionsService.new(object: object_acting_upon, ability: current_ability) %> | ||
<% depositor = permission_service.depositor %> | ||
|
||
<h2><%= t('.currently_sharing') %></h2> | ||
|
||
<table class="table table-bordered"> | ||
<tr> | ||
<th><%= t('.table_title_user') %></th> | ||
<th><div class="col-sm-10"><%= t('.table_title_access') %></div></th> | ||
</tr> | ||
<tr id="file_permissions"> | ||
<td> | ||
<%= label_tag :owner_access, class: "control-label" do %> | ||
Depositor (<span id="file_owner" data-depositor="<%= depositor %>"><%= link_to_profile depositor %></span>) | ||
<% end %> | ||
</td> | ||
<td> | ||
<div class="col-sm-10"> | ||
<%= Hyrax.config.owner_permission_levels.keys[0] %> | ||
</div> | ||
</td> | ||
</tr> | ||
<%= f.fields_for :permissions do |permission_fields| %> | ||
<% perm_hash = permission_fields.object.to_hash %> | ||
<% next if permission_service.excluded_permission?(perm_hash) %> | ||
<% cannot_edit_perms = permission_service.cannot_edit_permissions?(perm_hash) %> | ||
<tr> | ||
<td> | ||
<%= permission_fields.label :agent_name, class: "control-label" do %> | ||
<%= user_display_name_and_key(perm_hash[:name]) %> | ||
<% permission_service.unauthorized_collection_managers.select {|mgrs| mgrs[:name] == perm_hash[:name] }.each do |coll| %> | ||
<br />Access granted via collection <%= coll[:id] %> | ||
<% end %> | ||
<% end %> | ||
</td> | ||
<td> | ||
<div class="col-sm-10"> | ||
<% if cannot_edit_perms %> | ||
<%= Hyrax.config.permission_levels.key(perm_hash[:access]) %> | ||
<% else %> | ||
<%= permission_fields.select :access, Hyrax.config.permission_levels, {}, class: 'form-control select_perm' %> | ||
<% end %> | ||
</div> | ||
<% if !cannot_edit_perms %> | ||
<button class="btn close remove_perm" data-index="<%= permission_fields.index %>">×</button> | ||
<% end %> | ||
</td> | ||
</tr> | ||
<% end %> | ||
</table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<h2><%= t('.share_batch_with') %></h2> | ||
|
||
<table class="table table-bordered"> | ||
<tr id="file_permissions"> | ||
<th width="65%"><%= t('.table_title_user') %></th> | ||
<th><div class="col-sm-10"><%= t('.table_title_access') %></div></th> | ||
</tr> | ||
</table> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.