From 35272bf33a173ad2d9431e3ea8fc2f6badf08f9c Mon Sep 17 00:00:00 2001 From: Justin Coyne Date: Fri, 3 Nov 2023 15:32:52 -0500 Subject: [PATCH] Support embargo response from the media auth service So that the media viewer can display an appropriate message to the user --- app/models/stacks_media_stream.rb | 2 +- app/models/stacks_rights.rb | 2 + app/services/media_authentication_json.rb | 88 +++++----- spec/requests/media_auth_request_spec.rb | 158 +++++++++++------- .../media_authentication_json_spec.rb | 6 +- 5 files changed, 152 insertions(+), 104 deletions(-) diff --git a/app/models/stacks_media_stream.rb b/app/models/stacks_media_stream.rb index 24c1e8b8..1efd5194 100644 --- a/app/models/stacks_media_stream.rb +++ b/app/models/stacks_media_stream.rb @@ -17,5 +17,5 @@ def file def stacks_rights @stacks_rights ||= StacksRights.new(id: id, file_name: file_name) end - delegate :rights, :restricted_by_location?, :stanford_restricted?, to: :stacks_rights + delegate :rights, :restricted_by_location?, :stanford_restricted?, :embargoed?, to: :stacks_rights end diff --git a/app/models/stacks_rights.rb b/app/models/stacks_rights.rb index f406bcef..b0b21e64 100644 --- a/app/models/stacks_rights.rb +++ b/app/models/stacks_rights.rb @@ -33,6 +33,8 @@ def restricted_by_location? rights.restricted_by_location?(file_name) end + delegate :embargoed?, to: :rights + def object_thumbnail? doc = Nokogiri::XML.parse(public_xml) diff --git a/app/services/media_authentication_json.rb b/app/services/media_authentication_json.rb index e0348d55..e55996a6 100644 --- a/app/services/media_authentication_json.rb +++ b/app/services/media_authentication_json.rb @@ -4,6 +4,10 @@ # A class to model various authentication checks on media objects # and return a hash to be used as JSON in a controller response class MediaAuthenticationJson + # @param [User] user + # @param [StacksMediaStream] media + # @param [String] auth_url the login url to send to the client if the user could login. + # @param [Ability] ability the CanCan ability object def initialize(user:, media:, auth_url:, ability:) @user = user @media = media @@ -11,56 +15,64 @@ def initialize(user:, media:, auth_url:, ability:) @ability = ability end - def as_json(*) - return stanford_or_location_response if location_grants_access? && stanford_grants_access? - return location_only_response if location_grants_access? - return stanford_only_response if stanford_grants_access? + class DenyResponse + def initialize(auth_url) + @auth_url = auth_url + @status = [] + end - {} - end + attr_reader :status, :auth_url - private + def as_json + return { status: }.compact_blank.merge(login_service) if stanford_restricted? - attr_reader :auth_url, :media, :user, :ability + { status: }.compact_blank + end - def location_only_response - { - status: [:location_restricted] - } - end + def login_service + { + service: { + '@id' => auth_url, + 'label' => 'Stanford-affiliated? Login to play' + } + } + end - def stanford_only_response - { - status: [:stanford_restricted] - }.merge(login_service) - end + def stanford_restricted? + status.include?(:stanford_restricted) + end - def stanford_or_location_response - { - status: [ - :stanford_restricted, - :location_restricted - ] - }.merge(login_service) - end + def add_stanford_restricted! + status << :stanford_restricted + end - def login_service - { - service: { - '@id' => auth_url, - 'label' => 'Stanford-affiliated? Login to play' - } - } + def add_location_restricted! + status << :location_restricted + end + + def add_embargo! + status << :embargoed + end end - def stanford_restricted? - media.stanford_restricted? + def build_response + DenyResponse.new(auth_url).tap do |response| + response.add_stanford_restricted! if stanford_grants_access? + response.add_location_restricted! if location_grants_access? + response.add_embargo! if embargoed? + end end - def location_restricted? - media.restricted_by_location? + def as_json(*) + build_response.as_json end + private + + attr_reader :auth_url, :media, :user, :ability + + delegate :embargoed?, :stanford_restricted?, :restricted_by_location?, to: :media + def user_is_in_location? ability.can? :access, media end @@ -70,6 +82,6 @@ def stanford_grants_access? end def location_grants_access? - location_restricted? && !user_is_in_location? + restricted_by_location? && !user_is_in_location? end end diff --git a/spec/requests/media_auth_request_spec.rb b/spec/requests/media_auth_request_spec.rb index 69143cf1..551e13f6 100644 --- a/spec/requests/media_auth_request_spec.rb +++ b/spec/requests/media_auth_request_spec.rb @@ -3,81 +3,115 @@ require 'rails_helper' RSpec.describe "Authentication for Media requests", type: :request do - - let(:user_no_loc_no_webauth) { User.new } - let(:user_webauth_stanford_no_loc) { User.new(webauth_user: true, ldap_groups: %w(stanford:stanford)) } let(:druid) { 'bb582xs1304' } - describe "#auth_check" do - let(:format) { 'mp4' } - let(:public_xml) do - <<-XML - - #{rights_xml} - - XML + let(:format) { 'mp4' } + let(:public_xml) do + <<-XML + + #{rights_xml} + + XML + end + + let(:rights_xml) do + <<-EOF.strip_heredoc + + + + Stanford + + + + EOF + end + + let(:mock_media) do + sms = StacksMediaStream.new(id: 'bb582xs1304', file_name: 'file', format: format) + allow(Purl).to receive(:public_xml).with('bb582xs1304').and_return(public_xml) + sms + end + + before do + allow_any_instance_of(MediaController).to receive(:current_user).and_return(user) + allow_any_instance_of(MediaController).to receive(:current_media).and_return(mock_media) + end + + context 'when the user is stanford authenticated' do + let(:user) { User.new(webauth_user: true, ldap_groups: %w(stanford:stanford)) } + + it 'gets the success JSON and a token' do + get "/media/#{druid}/file.#{format}/auth_check" + expect(response.parsed_body['status']).to eq 'success' + expect(response.parsed_body['token']).to match(/^[%a-zA-Z0-9]+/) end + end + + context 'when the user is not authenticated' do + let(:user) { User.new } - let(:rights_xml) do - <<-EOF.strip_heredoc - - - - Stanford - - - - EOF + context 'stanford restricted' do + it 'indicates that the object is restricted in the json' do + get "/media/#{druid}/file.#{format}/auth_check" + expect(response.parsed_body['status']).to eq ['stanford_restricted'] + end end - let(:mock_media) do - sms = StacksMediaStream.new(id: 'bb582xs1304', file_name: 'file', format: format) - allow(Purl).to receive(:public_xml).with('bb582xs1304').and_return(public_xml) - sms + context 'location restricted' do + let(:rights_xml) do + <<-EOF.strip_heredoc + + + + location1 + + + + EOF + end + + it 'indicates that the object is location restricted in the json' do + get "/media/#{druid}/file.#{format}/auth_check" + expect(response.parsed_body['status']).to eq ['location_restricted'] + end end - context 'when the user can read/stream the file' do - it 'gets the success JSON and a token' do - allow_any_instance_of(MediaController).to receive(:current_user).and_return(user_webauth_stanford_no_loc) - allow_any_instance_of(MediaController).to receive(:current_media).and_return(mock_media) - get "/media/#{druid}/file.#{format}/auth_check.js" - body = JSON.parse(response.body) - expect(body['status']).to eq 'success' - expect(body['token']).to match(/^[%a-zA-Z0-9]+/) + context 'when the file is embargoed or stanford restricted' do + let(:rights_xml) do + <<-EOF.strip_heredoc + + + + 2099-05-15 + stanford + + + + EOF + end + + it 'indicates that the object is stanford restricted and embargoed in the json' do + get "/media/#{druid}/file.#{format}/auth_check" + expect(response.parsed_body['status']).to eq %w[stanford_restricted embargoed] end end - context 'when the user cannot read/stream the file' do - context 'stanford restricted' do - it 'indicates that the object is restricted in the json' do - allow_any_instance_of(MediaController).to receive(:current_user).and_return(user_no_loc_no_webauth) - allow_any_instance_of(MediaController).to receive(:current_media).and_return(mock_media) - get "/media/#{druid}/file.#{format}/auth_check.js" - body = JSON.parse(response.body) - expect(body['status']).to eq(['stanford_restricted']) - end + context 'when the file is embargoed' do + let(:rights_xml) do + <<-EOF.strip_heredoc + + + + 2099-05-15 + + + + EOF end - context 'location restricted' do - let(:rights_xml) do - <<-EOF.strip_heredoc - - - - location1 - - - - EOF - end - - it 'indicates that the object is location restricted in the json' do - allow_any_instance_of(MediaController).to receive(:current_user).and_return(user_no_loc_no_webauth) - allow_any_instance_of(MediaController).to receive(:current_media).and_return(mock_media) - get "/media/#{druid}/file.#{format}/auth_check.js" - body = JSON.parse(response.body) - expect(body['status']).to eq(['location_restricted']) - end + it 'indicates that the object is embargoed in the json' do + get "/media/#{druid}/file.#{format}/auth_check.js" + expect(response.parsed_body['status']).to eq ['embargoed'] end end end diff --git a/spec/services/media_authentication_json_spec.rb b/spec/services/media_authentication_json_spec.rb index 45f7bb52..2af9c62a 100644 --- a/spec/services/media_authentication_json_spec.rb +++ b/spec/services/media_authentication_json_spec.rb @@ -4,11 +4,11 @@ RSpec.describe MediaAuthenticationJson do let(:media) do - double( - 'Media', + instance_double( + StacksMediaStream, restricted_by_location?: false, stanford_restricted?: false, - location_rights: false + embargoed?: false ) end let(:ability) { Ability.new(user) }