Skip to content

Commit

Permalink
Merge pull request #1007 from sul-dlss/support-embargo
Browse files Browse the repository at this point in the history
Support embargo response from the media auth service
  • Loading branch information
aaron-collier authored Nov 3, 2023
2 parents 0be8f8b + 7035b4f commit c7218fc
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 104 deletions.
2 changes: 1 addition & 1 deletion app/models/stacks_media_stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ def file
def stacks_rights
@stacks_rights ||= StacksRights.new(id:, 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
2 changes: 2 additions & 0 deletions app/models/stacks_rights.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
89 changes: 51 additions & 38 deletions app/services/media_authentication_json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,76 @@
# 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
@auth_url = auth_url
@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?
# This JSON response is sent to the client when they are not authorized to view a stream.
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
Expand All @@ -70,6 +83,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
158 changes: 96 additions & 62 deletions spec/requests/media_auth_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
<publicObject>
#{rights_xml}
</publicObject>
XML
let(:format) { 'mp4' }
let(:public_xml) do
<<-XML
<publicObject>
#{rights_xml}
</publicObject>
XML
end

let(:rights_xml) do
<<-EOF.strip_heredoc
<rightsMetadata>
<access type="read">
<machine>
<group>Stanford</group>
</machine>
</access>
</rightsMetadata>
EOF
end

let(:mock_media) do
sms = StacksMediaStream.new(id: 'bb582xs1304', file_name: 'file', 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
<rightsMetadata>
<access type="read">
<machine>
<group>Stanford</group>
</machine>
</access>
</rightsMetadata>
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:)
allow(Purl).to receive(:public_xml).with('bb582xs1304').and_return(public_xml)
sms
context 'location restricted' do
let(:rights_xml) do
<<-EOF.strip_heredoc
<rightsMetadata>
<access type="read">
<machine>
<location>location1</location>
</machine>
</access>
</rightsMetadata>
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
<rightsMetadata>
<access type="read">
<machine>
<embargoReleaseDate>2099-05-15</embargoReleaseDate>
<group>stanford</group>
</machine>
</access>
</rightsMetadata>
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
<rightsMetadata>
<access type="read">
<machine>
<embargoReleaseDate>2099-05-15</embargoReleaseDate>
</machine>
</access>
</rightsMetadata>
EOF
end

context 'location restricted' do
let(:rights_xml) do
<<-EOF.strip_heredoc
<rightsMetadata>
<access type="read">
<machine>
<location>location1</location>
</machine>
</access>
</rightsMetadata>
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
Expand Down
6 changes: 3 additions & 3 deletions spec/services/media_authentication_json_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down

0 comments on commit c7218fc

Please sign in to comment.