From 406a0f676a7d041928f3fe8a2a0c7c05a28fe196 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 31 Mar 2023 10:46:16 -0400 Subject: [PATCH] Override ActiveFedora and HydraAccessControls to ensure AccessControl objects are marked dirty and autosave so they get indexed properly --- config/initializers/active_fedora_general.rb | 68 ++++++++++++++++++++ spec/models/media_object_spec.rb | 30 +++++++++ 2 files changed, 98 insertions(+) diff --git a/config/initializers/active_fedora_general.rb b/config/initializers/active_fedora_general.rb index 933d8aed38..6356772471 100644 --- a/config/initializers/active_fedora_general.rb +++ b/config/initializers/active_fedora_general.rb @@ -34,3 +34,71 @@ def content_disposition_filename ::RDF::URI.decode(filename) if filename end end + +# Overrides that ensure AccessControl objects are marked dirty when read/edit/discover groups/users are changed and autosave if dirty +# This fixes a timing bug where AccessControl objects are saved in an after_save callback which runs after normal indexing occurs +# See https://github.com/avalonmediasystem/avalon/issues/5140 + +# Override contained_rdf_sources to ensure AccessControl objects get autosaved when dirty +ActiveFedora::Base.class_eval do + private + def _create_record(_options = {}) + assign_rdf_subject + serialize_attached_files + @ldp_source = @ldp_source.create + assign_uri_to_contained_resources + save_contained_resources + save_access_control_resources + refresh + end + + def _update_record(_options = {}) + serialize_attached_files + execute_sparql_update + save_contained_resources + save_access_control_resources + refresh + end + + def save_access_control_resources + access_control_sources.changed.each do |_, resource| + resource.save + end + end + + def access_control_sources + @access_control_sources ||= ActiveFedora::AssociationHash.new(self, access_control_reflections) + end + + def access_control_reflections + self.class.reflect_on_all_associations(:belongs_to).select { |_, reflection| reflection.klass <= Hydra::AccessControl } + end +end + +# Enable dirty tracking for the permissions attribute +Rails.application.config.to_prepare do + Hydra::AccessControl.define_attribute_methods :permissions +end + +# Override set_entities to notify ActiveModel::Dirty dirty tracking that the permissions attribute is changing +Hydra::AccessControls::Permissions.module_eval do + private + # @param [Symbol] permission either :discover, :read or :edit + # @param [Symbol] type either :person or :group + # @param [Array] values Values to set + # @param [Array] changeable Values we are allowed to change + def set_entities(permission, type, values, changeable) + (changeable - values).each do |entity| + for_destroy = search_by_type_and_mode(type, permission_to_uri(permission)).select { |p| p.agent_name == entity } + access_control.permissions_will_change! + permissions.delete(for_destroy) + end + + values.each do |agent_name| + exists = search_by_type_and_mode(type, permission_to_uri(permission)).select { |p| p.agent_name == agent_name } + access_control.permissions_will_change! + permissions.build(name: agent_name, access: permission.to_s, type: type) unless exists.present? + end + end +end +# End of overrides for AccessControl dirty tracking and autosaving diff --git a/spec/models/media_object_spec.rb b/spec/models/media_object_spec.rb index 6825aa285c..238b1b32b5 100644 --- a/spec/models/media_object_spec.rb +++ b/spec/models/media_object_spec.rb @@ -1062,4 +1062,34 @@ end end end + + describe 'read_groups=' do + let(:solr_doc) { ActiveFedora::SolrService.query("id:#{media_object.id}").first } + + context 'when creating a MediaObject' do + let(:media_object) { FactoryBot.build(:media_object) } + + it 'saves and indexes' do + expect(media_object.read_groups).to be_empty + media_object.read_groups = ["ExternalGroup"] + expect(media_object.access_control).to be_changed + media_object.save + expect(media_object.reload.read_groups).to eq ["ExternalGroup"] + expect(solr_doc["read_access_group_ssim"]).to eq ["ExternalGroup"] + end + end + + context 'when updating a MediaObject' do + let(:media_object) { FactoryBot.create(:media_object) } + + it 'saves and indexes' do + expect(media_object.read_groups).to be_empty + media_object.read_groups = ["ExternalGroup"] + expect(media_object.access_control).to be_changed + media_object.save + expect(media_object.reload.read_groups).to eq ["ExternalGroup"] + expect(solr_doc["read_access_group_ssim"]).to eq ["ExternalGroup"] + end + end + end end