diff --git a/app/models/work_authorization.rb b/app/models/work_authorization.rb index e7cfc087e..520adc4a3 100644 --- a/app/models/work_authorization.rb +++ b/app/models/work_authorization.rb @@ -23,6 +23,27 @@ def initialize(user:, work_pid:) # This will be a non-ActiveRecord resource validates :work_pid, presence: true + ## + # @param user [User] + # @param authorize_until [Time] authorize the given work_pid(s) until this point in time. + # @param revoke_expirations_before [Time] expire all authorizations that have expires_at less than or equal to this parameter. + # @param work_pid [String, Array] + def self.handle_signin_for!(user:, authorize_until: 1.day.from_now, work_pid: nil, revoke_expirations_before: Time.zone.now) + # Maybe we get multiple pids; let's handle that accordingly + Array.wrap(work_pid).each do |pid| + begin + authorize!(user: user, work_pid: pid, expires_at: authorize_until) + rescue WorkNotFoundError + Rails.logger.info("Unable to find work_pid of #{pid.inspect}.") + end + end + + # We re-authorized the above work_pid, so it should not be in this query. + where("user_id = :user_id AND expires_at <= :expires_at", user_id: user.id, expires_at: revoke_expirations_before).pluck(:work_pid).each do |pid| + revoke!(user: user, work_pid: pid) + end + end + ## # Grant the given :user permission to read the work associated with the given :work_pid. # @@ -33,13 +54,14 @@ def initialize(user:, work_pid:) # # @see .revoke! # rubocop:disable Rails/FindBy - def self.authorize!(user:, work_pid:) + def self.authorize!(user:, work_pid:, expires_at: 1.day.from_now) work = ActiveFedora::Base.where(id: work_pid).first raise WorkNotFoundError.new(user: user, work_pid: work_pid) unless work transaction do authorization = find_or_create_by!(user_id: user.id, work_pid: work.id) - authorization.update!(work_title: work.title) + authorization.update!(work_title: work.title, expires_at: expires_at) + work.set_read_users([user.user_key], [user.user_key]) work.save! end diff --git a/spec/models/work_authorization_spec.rb b/spec/models/work_authorization_spec.rb index 16ea9440b..cb73996e5 100644 --- a/spec/models/work_authorization_spec.rb +++ b/spec/models/work_authorization_spec.rb @@ -4,9 +4,40 @@ RSpec.describe WorkAuthorization, type: :model do let(:work) { FactoryBot.create(:generic_work) } + let(:other_work) { FactoryBot.create(:generic_work) } let(:borrowing_user) { FactoryBot.create(:user) } let(:ability) { ::Ability.new(borrowing_user) } + describe '.handle_signin_for!' do + context 'when given a work_pid' do + it 'will re-authorize the given work_pid and expire non-specified work_ids' do + described_class.authorize!(user: borrowing_user, work_pid: work.id, expires_at: 1.day.ago) + described_class.authorize!(user: borrowing_user, work_pid: other_work.id, expires_at: 1.day.ago) + + expect do + expect do + described_class.handle_signin_for!(user: borrowing_user, work_pid: work.id, authorize_until: 1.day.from_now) + end.not_to change { ::Ability.new(borrowing_user).can?(:read, work.id) }.from(true) + end.to change { ::Ability.new(borrowing_user).can?(:read, other_work.id) }.from(true).to(false) + end + end + + context 'when not given a work_pid' do + it 'will de-authorize all authorizations that have expired but not those that have not expired' do + # Note: This one is expiring in the future + described_class.authorize!(user: borrowing_user, work_pid: work.id, expires_at: 2.days.from_now) + # Note: We'll be expiring this one. + described_class.authorize!(user: borrowing_user, work_pid: other_work.id, expires_at: 1.day.ago) + + expect do + expect do + described_class.handle_signin_for!(user: borrowing_user, revoke_expirations_before: Time.zone.now) + end.not_to change { ::Ability.new(borrowing_user).can?(:read, work.id) }.from(true) + end.to change { ::Ability.new(borrowing_user).can?(:read, other_work.id) }.from(true).to(false) + end + end + end + describe '.authorize!' do it 'gives the borrowing user the ability to "read" the work' do # We re-instantiate an ability class because CanCan caches many of the ability checks. By