Skip to content

Commit

Permalink
Allow using cocina json as the data source
Browse files Browse the repository at this point in the history
This is based on a global feature flag, so we can enable this via shared configs for testing
  • Loading branch information
jcoyne committed Nov 8, 2023
1 parent 089769d commit 06f9643
Show file tree
Hide file tree
Showing 11 changed files with 719 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ Rails:
Metrics/CyclomaticComplexity:
Exclude:
- 'app/models/ability.rb'
- 'app/models/cocina_ability.rb'

Metrics/PerceivedComplexity:
Exclude:
- 'app/models/ability.rb'
- 'app/models/cocina_ability.rb'

Metrics/AbcSize:
Exclude:
- 'app/models/ability.rb'
- 'app/models/cocina_ability.rb'
- 'app/controllers/iiif_controller.rb'

Layout/EmptyLinesAroundBlockBody:
Expand Down
29 changes: 11 additions & 18 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2023-11-03 20:55:41 UTC using RuboCop version 1.57.2.
# on 2023-11-08 16:01:02 UTC using RuboCop version 1.57.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
Expand Down Expand Up @@ -28,9 +28,9 @@ Metrics/CyclomaticComplexity:
# Offense count: 13
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
Max: 25
Max: 50

# Offense count: 35
# Offense count: 31
RSpec/AnyInstance:
Exclude:
- 'spec/controllers/media_controller_spec.rb'
Expand All @@ -43,7 +43,7 @@ RSpec/AnyInstance:
- 'spec/requests/iiif_spec.rb'
- 'spec/requests/media_auth_request_spec.rb'

# Offense count: 89
# Offense count: 78
# Configuration parameters: Prefixes, AllowedPatterns.
# Prefixes: when, with, without
RSpec/ContextWording:
Expand Down Expand Up @@ -80,7 +80,7 @@ RSpec/EmptyLineAfterExampleGroup:
- 'spec/controllers/legacy_image_service_controller_spec.rb'
- 'spec/requests/file_auth_request_spec.rb'

# Offense count: 37
# Offense count: 32
# This cop supports safe autocorrection (--autocorrect).
RSpec/EmptyLineAfterFinalLet:
Exclude:
Expand Down Expand Up @@ -159,11 +159,11 @@ RSpec/MessageSpies:
- 'spec/controllers/file_controller_spec.rb'
- 'spec/controllers/media_controller_spec.rb'

# Offense count: 67
# Offense count: 66
RSpec/MultipleExpectations:
Max: 12

# Offense count: 81
# Offense count: 80
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
# SupportedStyles: always, named_only
RSpec/NamedSubject:
Expand All @@ -183,7 +183,7 @@ RSpec/NamedSubject:
- 'spec/services/iiif_metadata_service_spec.rb'
- 'spec/services/media_authentication_json_spec.rb'

# Offense count: 66
# Offense count: 54
# Configuration parameters: AllowedGroups.
RSpec/NestedGroups:
Max: 6
Expand Down Expand Up @@ -217,6 +217,7 @@ RSpec/VerifiedDoubles:
- 'spec/services/media_authentication_json_spec.rb'

# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: slashes, arguments
Rails/FilePath:
Expand Down Expand Up @@ -246,7 +247,7 @@ Style/HashAsLastArrayItem:
Exclude:
- 'app/controllers/object_controller.rb'

# Offense count: 18
# Offense count: 13
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
Expand All @@ -266,17 +267,9 @@ Style/StringConcatenation:
- 'spec/controllers/object_controller_spec.rb'
- 'spec/models/stacks_media_token_spec.rb'

# Offense count: 4
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: MinSize.
# SupportedStyles: percent, brackets
Style/SymbolArray:
EnforcedStyle: brackets

# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
# AllowedMethods: define_method
Style/SymbolProc:
Exclude:
- 'app/controllers/webauth_controller.rb'
119 changes: 119 additions & 0 deletions app/models/cocina_ability.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# frozen_string_literal: true

##
# User authentication
class CocinaAbility
include CanCan::Ability

def initialize(user)
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
# if user.admin?
# can :manage, :all
# else
# can :read, :all
# end
#
# The first argument to `can` is the action you are giving the user
# permission to do.
# If you pass :manage it will apply to every action. Other common actions
# here are :read, :create, :update and :destroy.
#
# The second argument is the resource the user can perform the action on.
# If you pass :all, it will apply to every resource. Otherwise, pass a Ruby
# class of the resource.
#
# The third argument is an optional hash of conditions to further filter the
# objects. For example, here the user can only update published articles.
# can :update, Article, :published => true
#
# The block argument takes as a parameter an instance of the object for which
# permission is being checked. If the block returns true, the user is granted that
# ability, otherwise the user is denied that ability. The block is only evaluated
# for instances of objects, *not* for classes. If a class is passed to a `can?` or a
# `cannot?` that's defined by a block, that check will *always* grant permission.
#
# See the wiki for details:
# https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
# https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities-with-Blocks

# NOTE: the below ability definitions which reference StacksFile also implicitly
# cover StacksImage and StacksMediaStream, and any other subclasses of StacksFile.

downloadable_models = [StacksFile, StacksImage]
access_models = downloadable_models + [StacksMediaStream]

can :download, downloadable_models do |f|
f.cocina_rights.download == 'world'
end

can [:access], access_models do |f|
f.cocina_rights.view == 'world'
end

if user.stanford?
can :download, downloadable_models do |f|
f.cocina_rights.download == 'stanford'
end

can [:access], access_models do |f|
f.cocina_rights.view == 'stanford'
end
end

if user.locations.present?
can :download, downloadable_models do |f|
next unless f.cocina_rights.download == 'location-based'

user.locations.include?(f.cocina_rights.location)
end

can [:access], access_models do |f|
user.locations.any? do |_location|
next unless f.cocina_rights.view == 'location-based'

user.locations.include?(f.cocina_rights.location)
end
end
end

if user.cdl_tokens.present?
# TODO: Actually check if the CDL object is downloadable
# can [:download, :read], models do |f|
# ...
# end

can [:access], access_models do |f|
next unless f.cocina_rights.controlled_digital_lending?

user.cdl_tokens.any? { |payload| payload['aud'] == f.id }
end
end

cannot :download, RestrictedImage

# These are called when checking to see if the image response should be served
can [:download, :read], Projection do |projection|
can?(:download, projection.image)
end

can [:download, :read], Projection do |projection|
# Allow access to tile or thumbnail-sized requests for an accessible image
(projection.tile? || projection.thumbnail?) && can?(:access, projection.image)
end

can :access, Projection do |projection|
can?(:access, projection.image)
end

can :read, Projection do |projection|
# Allow access to thumbnail-sized projections of a declared (or implicit) thumbnail for the object;
# note that because this is implicit, we do not check rightsMetadata permissions.
projection.thumbnail? && projection.object_thumbnail?
end

alias_action :stream, to: :access
can :read_metadata, StacksImage
end
end
24 changes: 24 additions & 0 deletions app/models/cocina_rights.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

# The rights derived from cocina
class CocinaRights
def initialize(file_rights)
@file_rights = file_rights
end

def download
@file_rights['download']
end

def view
@file_rights['view']
end

def controlled_digital_lending?
@file_rights['controlledDigitalLending']
end

def location
@file_rights['location']
end
end
34 changes: 32 additions & 2 deletions app/models/purl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def self.instance
end

class << self
delegate :public_xml, :files, :barcode, to: :instance
delegate :public_xml, :public_json, :files, :barcode, to: :instance
end

# TODO: was etag a valid key?
Expand All @@ -26,9 +26,35 @@ def public_xml(druid)
end
end

def files(druid)
def public_json(druid)
Rails.cache.fetch("purl/#{druid}.json", expires_in: 10.minutes) do
benchmark "Fetching public json for #{druid}" do
response = Faraday.get(public_json_url(druid))
raise Purl::Exception, response.status unless response.success?

JSON.parse(response.body)
end
end
end

def files(druid, &block)
return to_enum(:files, druid) unless block_given?

Settings.features.cocina ? files_from_json(druid, &block) : files_from_xml(druid, &block)
end

def files_from_json(druid)
doc = public_json(druid)

doc.dig('structural', 'contains').each do |fileset|
fileset.dig('structural', 'contains').each do |file|
file = StacksFile.new(id: druid, file_name: file['filename'])
yield file
end
end
end

def files_from_xml(druid)
doc = Nokogiri::XML.parse(public_xml(druid))

doc.xpath('//contentMetadata/resource').each do |resource|
Expand Down Expand Up @@ -56,6 +82,10 @@ def public_xml_url(druid)
Settings.purl.url + "#{druid}.xml"
end

def public_json_url(druid)
"#{Settings.purl.url}#{druid}.json"
end

def logger
Rails.logger
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/stacks_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ def druid_parts
def stacks_rights
@stacks_rights ||= StacksRights.new(id:, file_name:)
end
delegate :rights, to: :stacks_rights
delegate :rights, :cocina_rights, to: :stacks_rights
end
2 changes: 1 addition & 1 deletion app/models/stacks_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ def info_service
def stacks_rights
@stacks_rights ||= StacksRights.new(id:, file_name:)
end
delegate :rights, :maybe_downloadable?, :object_thumbnail?,
delegate :rights, :cocina_rights, :maybe_downloadable?, :object_thumbnail?,
:stanford_restricted?, :restricted_by_location?, :cdl_restricted?, to: :stacks_rights
end
3 changes: 2 additions & 1 deletion app/models/stacks_media_stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def file
def stacks_rights
@stacks_rights ||= StacksRights.new(id:, file_name:)
end
delegate :rights, :restricted_by_location?, :stanford_restricted?, :embargoed?,

delegate :rights, :cocina_rights, :restricted_by_location?, :stanford_restricted?, :embargoed?,
:embargo_release_date, to: :stacks_rights
end
Loading

0 comments on commit 06f9643

Please sign in to comment.