Skip to content
This repository has been archived by the owner on Jan 5, 2021. It is now read-only.

Commit

Permalink
Merge ce2405a into a989924
Browse files Browse the repository at this point in the history
  • Loading branch information
awead committed May 30, 2019
2 parents a989924 + ce2405a commit 00c0feb
Show file tree
Hide file tree
Showing 44 changed files with 1,199 additions and 103 deletions.
1 change: 1 addition & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ exclude_patterns:
- "app/cho/transaction/operations/file/process.rb"
- "app/cho/transaction/shared/save_with_change_set.rb"
- "app/cho/transaction/shared/save_with_resource.rb"
- "app/blacklight/access_controls/edit_ability.rb"
89 changes: 89 additions & 0 deletions app/blacklight/access_controls/edit_ability.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# frozen_string_literal: true

# Overrides existing methods in Blacklight::AccessControls::Ability and adds new methods to provide edit abilities
# to users and groups. Abilities form a hierarchy such that each success ability encompasses the previous.
#
# @example
# (discover) -> (read) -> (download) -> (edit)
#
# Discover access is the "lowest" ability, where edit is the "highest." An edit ability includes the abilities
# of the previous three: download, read, and discover; a download ability includes the abilities of the previous
# two: read and discover; and a read ability also includes the discover ability.
module AccessControls::EditAbility
extend ActiveSupport::Concern

def edit_permissions
can :edit, String do |id|
test_edit(id)
end

can :edit, SolrDocument do |obj|
cache.put(obj.id, obj)
test_edit(obj.id)
end
end

def test_edit(id)
Rails.logger.debug("[CANCAN] Checking edit permissions for user: #{current_user.user_key} with groups: #{user_groups.inspect}")
group_intersection = user_groups & edit_groups(id)
!group_intersection.empty? || edit_users(id).include?(current_user.user_key)
end

# download and edit access implies read access, so read_groups is the union of download, edit, and read groups.
def read_groups(id)
doc = permissions_doc(id)
return [] if doc.nil?

rg = download_groups(id) | edit_groups(id) | Array(doc[self.class.read_group_field])
Rails.logger.debug("[CANCAN] read_groups: #{rg.inspect}")
rg
end

# download and edit access implies read access, so read_users is the union of download, edit, and read users.
def read_users(id)
doc = permissions_doc(id)
return [] if doc.nil?

rp = download_users(id) | edit_users(id) | Array(doc[self.class.read_user_field])
Rails.logger.debug("[CANCAN] read_users: #{rp.inspect}")
rp
end

# edit access implies download access, so download_groups is the union of edit and download groups
def download_groups(id)
doc = permissions_doc(id)
return [] if doc.nil?

dg = edit_groups(id) | Array(doc[self.class.download_group_field])
Rails.logger.debug("[CANCAN] download_groups: #{dg.inspect}")
dg
end

# edit access implies download access, so download_users is the union of edit and download users
def download_users(id)
doc = permissions_doc(id)
return [] if doc.nil?

dp = edit_users(id) | Array(doc[self.class.download_user_field])
Rails.logger.debug("[CANCAN] download_users: #{dp.inspect}")
dp
end

def edit_groups(id)
doc = permissions_doc(id)
return [] if doc.nil?

eg = Array(doc[Blacklight::AccessControls.config.edit_group_field])
Rails.logger.debug("[CANCAN] edit_groups: #{eg.inspect}")
eg
end

def edit_users(id)
doc = permissions_doc(id)
return [] if doc.nil?

ep = Array(doc[Blacklight::AccessControls.config.edit_user_field])
Rails.logger.debug("[CANCAN] edit_users: #{ep.inspect}")
ep
end
end
13 changes: 13 additions & 0 deletions app/blacklight/access_controls/search_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

# Overrides the search builder provided by Blacklight::AccessControls to add additional filter queries to
# the Solr query build during search results. The field names are defined in AccessControls::WithEditFields
# and are used to restrict search results to items that the user has edit access to, as well as the existing
# fields for read and discover.
module AccessControls
class SearchBuilder < Blacklight::AccessControls::SearchBuilder
def default_permission_types
%w[discover read edit]
end
end
end
14 changes: 14 additions & 0 deletions app/blacklight/access_controls/with_edit_fields.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

# Module designed to be prepended to Blacklight::AccessControls::Config to allow for edit users and groups.
module AccessControls::WithEditFields
attr_writer :edit_group_field, :edit_user_field

def edit_group_field
@edit_group_field || 'edit_access_group_ssim'
end

def edit_user_field
@edit_user_field || 'edit_access_person_ssim'
end
end
9 changes: 8 additions & 1 deletion app/blacklight/search_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ def initialize(config:, user_params: {}, search_builder_class: config.search_bui
@current_ability = current_ability
end

# @return [SearchBuilder]
# @note if the user is an administrator, the base SearchBuilder is returned which does not enforce
# any access controls.
def search_builder
Blacklight::AccessControls::SearchBuilder.new(self, ability: current_ability)
if current_ability.admin?
::SearchBuilder.new(self)
else
::AccessControls::SearchBuilder.new(self, ability: current_ability)
end
end
end
1 change: 0 additions & 1 deletion app/cho/collection/archival.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# Collections are indexed both in Solr and and Postgres using the {IndexingAdapter}.
module Collection
class Archival < Valkyrie::Resource
include Repository::Access::ResourceControls
include CommonFields
include CommonQueries
include WithMembers
Expand Down
1 change: 1 addition & 0 deletions app/cho/collection/change_set_behaviors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Collection::ChangeSetBehaviors
extend ActiveSupport::Concern
include DataDictionary::FieldsForChangeSet
include Work::WithErrorMessages
include Repository::Access::ChangeSetBehaviors

included do
property :workflow, multiple: false, required: true
Expand Down
1 change: 1 addition & 0 deletions app/cho/collection/common_fields.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
module Collection::CommonFields
extend ActiveSupport::Concern
include DataDictionary::FieldsForObject
include Repository::Access::ResourceControls

WORKFLOW = ['default', 'mediated'].freeze

Expand Down
1 change: 0 additions & 1 deletion app/cho/collection/curated.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# Collections are indexed both in Solr and and Postgres using the {IndexingAdapter}.
module Collection
class Curated < Valkyrie::Resource
include Repository::Access::ResourceControls
include CommonFields
include CommonQueries
include WithMembers
Expand Down
1 change: 0 additions & 1 deletion app/cho/collection/library.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# Collections are indexed both in Solr and and Postgres using the {IndexingAdapter}.
module Collection
class Library < Valkyrie::Resource
include Repository::Access::ResourceControls
include CommonFields
include CommonQueries
include WithMembers
Expand Down
21 changes: 21 additions & 0 deletions app/cho/repository/access/change_set_behaviors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Repository::Access::ChangeSetBehaviors
extend ActiveSupport::Concern

included do
property :system_creator, multiple: false, required: true
validates :system_creator, presence: true

property :current_user, multiple: false, required: false, virtual: true

property :discover_groups, multiple: true, required: false
property :discover_users, multiple: true, required: false
property :read_groups, multiple: true, required: false
property :read_users, multiple: true, required: false
property :download_groups, multiple: true, required: false
property :download_users, multiple: true, required: false
property :edit_users, multiple: true, required: false
property :edit_groups, multiple: true, required: false
end
end
4 changes: 4 additions & 0 deletions app/cho/repository/access/resource_controls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module Repository::Access::ResourceControls
extend ActiveSupport::Concern

DEFAULT_SYSTEM_USER = 'system'

included do
# These are attributes defined in Blacklight's access controls, which are based off of Hydra's
attribute :discover_groups, Valkyrie::Types::Set
Expand All @@ -15,5 +17,7 @@ module Repository::Access::ResourceControls
# Edit attributes found in Hydra, but not in Blacklight
attribute :edit_users, Valkyrie::Types::Set
attribute :edit_groups, Valkyrie::Types::Set

attribute :system_creator, Valkyrie::Types::String.default(DEFAULT_SYSTEM_USER)
end
end
1 change: 1 addition & 0 deletions app/cho/shared/valkyrie_controller_behaviors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def initialize_change_set(**args)
end

def validate_save_and_respond(change_set, error_view)
change_set.current_user = current_user if change_set.try(:current_user)
updated_change_set = change_set_persister.validate_and_save(
change_set: change_set,
resource_params: resource_params
Expand Down
17 changes: 17 additions & 0 deletions app/cho/transaction/operations/access_controls/access_level.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Transaction::Operations::AccessControls
class AccessLevel
include Dry::Transaction::Operation

def call(change_set)
return Success(change_set) if change_set.try(:access_level).blank?

remaining_groups = change_set.read_groups - Repository::AccessLevel.names
change_set.read_groups = remaining_groups + [change_set.access_level]
Success(change_set)
rescue StandardError => e
Failure(Transaction::Rejection.new('Error applying access level', e))
end
end
end
18 changes: 18 additions & 0 deletions app/cho/transaction/operations/access_controls/permissions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Transaction::Operations::AccessControls
class Permissions
include Dry::Transaction::Operation

# @return [Valkyrie::ChangeSet]
# @note duplicate members can be removed by the underlying resource model
def call(change_set)
return Success(change_set) unless change_set.class.ancestors.include?(Repository::Access::ChangeSetBehaviors)

change_set.edit_users += [change_set.system_creator]
Success(change_set)
rescue StandardError => e
Failure(Transaction::Rejection.new('Error applying permissions', e))
end
end
end
23 changes: 23 additions & 0 deletions app/cho/transaction/operations/access_controls/system_creator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Transaction::Operations::AccessControls
class SystemCreator
include Dry::Transaction::Operation

def call(change_set)
return Success(change_set) if change_set.persisted?

updated_change_set = add_system_creator(change_set)
Success(updated_change_set)
rescue StandardError => e
Failure(Transaction::Rejection.new('Error applying system creator', e))
end

def add_system_creator(change_set)
return change_set unless change_set.try(:current_user)

change_set.system_creator = change_set.current_user.login
change_set
end
end
end
15 changes: 15 additions & 0 deletions app/cho/transaction/operations/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ class Container
register 'apply_access_level' do
Operations::Shared::ApplyAccessLevel.new
end
register 'apply_system_creator' do
Operations::Shared::ApplySystemCreator.new
end
end

namespace 'access_controls' do
register 'access_level' do
Operations::AccessControls::AccessLevel.new
end
register 'system_creator' do
Operations::AccessControls::SystemCreator.new
end
register 'permissions' do
Operations::AccessControls::Permissions.new
end
end

namespace 'import' do
Expand Down
21 changes: 0 additions & 21 deletions app/cho/transaction/operations/shared/apply_access_level.rb

This file was deleted.

4 changes: 3 additions & 1 deletion app/cho/transaction/shared/save_with_change_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ class SaveWithChangeSet
step :validate, with: 'shared.validate'
step :process_file, with: 'file.process'
step :import_work, with: 'import.work'
step :apply_access_level, with: 'shared.apply_access_level'
step :access_level, with: 'access_controls.access_level'
step :system_creator, with: 'access_controls.system_creator'
step :permissions, with: 'access_controls.permissions'
step :save, with: 'shared.save'
end
end
Expand Down
1 change: 1 addition & 0 deletions app/cho/work/file_set_change_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class FileSetChangeSet < Valkyrie::ChangeSet
include WithValidMembers
include WithFormFields
include Work::WithErrorMessages
include Repository::Access::ChangeSetBehaviors

delegate :url_helpers, to: 'Rails.application.routes'

Expand Down
1 change: 1 addition & 0 deletions app/cho/work/submission_change_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class SubmissionChangeSet < Valkyrie::ChangeSet
include WithValidMembers
include WithFormFields
include Work::WithErrorMessages
include Repository::Access::ChangeSetBehaviors

delegate :url_helpers, to: 'Rails.application.routes'

Expand Down
2 changes: 2 additions & 0 deletions app/devise/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
class Ability
include CanCan::Ability
include Blacklight::AccessControls::Ability
include AccessControls::EditAbility

delegate :admin?, to: :current_user

self.ability_logic += %i[
base_permissions
edit_permissions
admin_permissions
cannot_delete_agent_with_members
]
Expand Down
5 changes: 5 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ class Application < Rails::Application
config.storage_directory = Pathname.new(ENV['storage_directory']).expand_path
config.network_ingest_directory = Pathname.new(ENV['network_ingest_directory']).expand_path
config.extraction_directory = Pathname.new(ENV['extraction_directory']).expand_path

# Inject new behaviors into existing classes without having to override the entire class itself.
config.to_prepare do
Blacklight::AccessControls::Config.prepend AccessControls::WithEditFields
end
end
end

0 comments on commit 00c0feb

Please sign in to comment.