ddr-core 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +12 -0
  3. data/README.md +27 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/config/ddr_core_manifest.js +0 -0
  6. data/app/controllers/users/omniauth_callbacks_controller.rb +11 -0
  7. data/app/controllers/users/sessions_controller.rb +15 -0
  8. data/app/models/concerns/ddr/captionable.rb +25 -0
  9. data/app/models/concerns/ddr/describable.rb +108 -0
  10. data/app/models/concerns/ddr/governable.rb +25 -0
  11. data/app/models/concerns/ddr/has_admin_metadata.rb +141 -0
  12. data/app/models/concerns/ddr/has_attachments.rb +10 -0
  13. data/app/models/concerns/ddr/has_children.rb +10 -0
  14. data/app/models/concerns/ddr/has_content.rb +132 -0
  15. data/app/models/concerns/ddr/has_extracted_text.rb +10 -0
  16. data/app/models/concerns/ddr/has_intermediate_file.rb +25 -0
  17. data/app/models/concerns/ddr/has_multires_image.rb +14 -0
  18. data/app/models/concerns/ddr/has_parent.rb +18 -0
  19. data/app/models/concerns/ddr/has_struct_metadata.rb +21 -0
  20. data/app/models/concerns/ddr/has_thumbnail.rb +33 -0
  21. data/app/models/concerns/ddr/solr_document_behavior.rb +429 -0
  22. data/app/models/concerns/ddr/streamable.rb +25 -0
  23. data/app/models/ddr/admin_set.rb +28 -0
  24. data/app/models/ddr/attachment.rb +14 -0
  25. data/app/models/ddr/collection.rb +28 -0
  26. data/app/models/ddr/component.rb +31 -0
  27. data/app/models/ddr/contact.rb +23 -0
  28. data/app/models/ddr/digest.rb +8 -0
  29. data/app/models/ddr/file.rb +40 -0
  30. data/app/models/ddr/item.rb +36 -0
  31. data/app/models/ddr/language.rb +31 -0
  32. data/app/models/ddr/media_type.rb +22 -0
  33. data/app/models/ddr/resource.rb +94 -0
  34. data/app/models/ddr/rights_statement.rb +25 -0
  35. data/app/models/ddr/target.rb +17 -0
  36. data/config/initializers/devise.rb +262 -0
  37. data/config/locales/ddr-core.en.yml +85 -0
  38. data/config/routes.rb +3 -0
  39. data/db/migrate/20141104181418_create_users.rb +34 -0
  40. data/db/migrate/20141107124012_add_columns_to_user.rb +46 -0
  41. data/lib/ddr-core.rb +1 -0
  42. data/lib/ddr/auth.rb +80 -0
  43. data/lib/ddr/auth/ability.rb +18 -0
  44. data/lib/ddr/auth/ability_definitions.rb +26 -0
  45. data/lib/ddr/auth/ability_definitions/admin_set_ability_definitions.rb +9 -0
  46. data/lib/ddr/auth/ability_definitions/alias_ability_definitions.rb +23 -0
  47. data/lib/ddr/auth/ability_definitions/attachment_ability_definitions.rb +13 -0
  48. data/lib/ddr/auth/ability_definitions/collection_ability_definitions.rb +28 -0
  49. data/lib/ddr/auth/ability_definitions/component_ability_definitions.rb +13 -0
  50. data/lib/ddr/auth/ability_definitions/item_ability_definitions.rb +13 -0
  51. data/lib/ddr/auth/ability_definitions/lock_ability_definitions.rb +13 -0
  52. data/lib/ddr/auth/ability_definitions/publication_ability_definitions.rb +16 -0
  53. data/lib/ddr/auth/ability_definitions/role_based_ability_definitions.rb +39 -0
  54. data/lib/ddr/auth/ability_definitions/superuser_ability_definitions.rb +9 -0
  55. data/lib/ddr/auth/ability_factory.rb +10 -0
  56. data/lib/ddr/auth/abstract_ability.rb +48 -0
  57. data/lib/ddr/auth/affiliation.rb +14 -0
  58. data/lib/ddr/auth/affiliation_groups.rb +20 -0
  59. data/lib/ddr/auth/anonymous_ability.rb +7 -0
  60. data/lib/ddr/auth/auth_context.rb +109 -0
  61. data/lib/ddr/auth/auth_context_factory.rb +13 -0
  62. data/lib/ddr/auth/detached_auth_context.rb +19 -0
  63. data/lib/ddr/auth/dynamic_groups.rb +13 -0
  64. data/lib/ddr/auth/effective_permissions.rb +12 -0
  65. data/lib/ddr/auth/effective_roles.rb +9 -0
  66. data/lib/ddr/auth/failure_app.rb +16 -0
  67. data/lib/ddr/auth/group.rb +40 -0
  68. data/lib/ddr/auth/grouper_gateway.rb +70 -0
  69. data/lib/ddr/auth/groups.rb +32 -0
  70. data/lib/ddr/auth/ldap_gateway.rb +74 -0
  71. data/lib/ddr/auth/permissions.rb +18 -0
  72. data/lib/ddr/auth/remote_groups.rb +14 -0
  73. data/lib/ddr/auth/role_based_access_controls_enforcement.rb +56 -0
  74. data/lib/ddr/auth/roles.rb +28 -0
  75. data/lib/ddr/auth/roles/role.rb +121 -0
  76. data/lib/ddr/auth/roles/role_type.rb +23 -0
  77. data/lib/ddr/auth/roles/role_types.rb +52 -0
  78. data/lib/ddr/auth/superuser_ability.rb +7 -0
  79. data/lib/ddr/auth/test_helpers.rb +22 -0
  80. data/lib/ddr/auth/user.rb +54 -0
  81. data/lib/ddr/auth/web_auth_context.rb +29 -0
  82. data/lib/ddr/core.rb +110 -0
  83. data/lib/ddr/core/engine.rb +8 -0
  84. data/lib/ddr/core/version.rb +5 -0
  85. data/lib/ddr/error.rb +16 -0
  86. data/lib/ddr/files.rb +13 -0
  87. data/lib/ddr/fits.rb +189 -0
  88. data/lib/ddr/index.rb +29 -0
  89. data/lib/ddr/index/abstract_query_result.rb +22 -0
  90. data/lib/ddr/index/connection.rb +38 -0
  91. data/lib/ddr/index/csv_query_result.rb +84 -0
  92. data/lib/ddr/index/document_builder.rb +9 -0
  93. data/lib/ddr/index/field.rb +35 -0
  94. data/lib/ddr/index/field_attribute.rb +22 -0
  95. data/lib/ddr/index/fields.rb +154 -0
  96. data/lib/ddr/index/filter.rb +139 -0
  97. data/lib/ddr/index/query.rb +82 -0
  98. data/lib/ddr/index/query_builder.rb +185 -0
  99. data/lib/ddr/index/query_clause.rb +112 -0
  100. data/lib/ddr/index/query_params.rb +40 -0
  101. data/lib/ddr/index/query_result.rb +102 -0
  102. data/lib/ddr/index/response.rb +30 -0
  103. data/lib/ddr/index/sort_order.rb +28 -0
  104. data/lib/ddr/index/unique_key_field.rb +12 -0
  105. data/lib/ddr/managers.rb +9 -0
  106. data/lib/ddr/managers/manager.rb +13 -0
  107. data/lib/ddr/managers/technical_metadata_manager.rb +141 -0
  108. data/lib/ddr/structure.rb +188 -0
  109. data/lib/ddr/structures/agent.rb +49 -0
  110. data/lib/ddr/structures/component_type_term.rb +29 -0
  111. data/lib/ddr/structures/div.rb +64 -0
  112. data/lib/ddr/structures/f_locat.rb +54 -0
  113. data/lib/ddr/structures/file.rb +52 -0
  114. data/lib/ddr/structures/file_grp.rb +35 -0
  115. data/lib/ddr/structures/file_sec.rb +22 -0
  116. data/lib/ddr/structures/fptr.rb +31 -0
  117. data/lib/ddr/structures/mets_hdr.rb +37 -0
  118. data/lib/ddr/structures/mptr.rb +49 -0
  119. data/lib/ddr/structures/struct_map.rb +40 -0
  120. data/lib/ddr/utils.rb +185 -0
  121. data/lib/ddr/vocab.rb +22 -0
  122. data/lib/ddr/vocab/asset.rb +51 -0
  123. data/lib/ddr/vocab/contact.rb +9 -0
  124. data/lib/ddr/vocab/display.rb +9 -0
  125. data/lib/ddr/vocab/duke_terms.rb +13 -0
  126. data/lib/ddr/vocab/rdf_vocabulary_parser.rb +43 -0
  127. data/lib/ddr/vocab/roles.rb +25 -0
  128. data/lib/ddr/vocab/sources/duketerms.rdf +870 -0
  129. data/lib/ddr/vocab/vocabulary.rb +37 -0
  130. data/lib/ddr/workflow.rb +8 -0
  131. data/lib/tasks/ddr/core_tasks.rake +4 -0
  132. metadata +428 -0
@@ -0,0 +1,9 @@
1
+ module Ddr::Auth
2
+ class SuperuserAbilityDefinitions < AbilityDefinitions
3
+
4
+ def call
5
+ can :manage, :all
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Ddr::Auth
2
+ class AbilityFactory
3
+
4
+ def self.call(user = nil, env = nil)
5
+ auth_context = AuthContextFactory.call(user, env)
6
+ auth_context.ability
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,48 @@
1
+ require 'cancan'
2
+
3
+ module Ddr::Auth
4
+ # @abstract
5
+ class AbstractAbility
6
+ include CanCan::Ability
7
+
8
+ class_attribute :ability_definitions
9
+ self.ability_definitions = []
10
+
11
+ # CanCan default aliases:
12
+ #
13
+ # alias_action :index, :show, :to => :read
14
+ # alias_action :new, :to => :create
15
+ # alias_action :edit, :to => :update
16
+ #
17
+ class_attribute :exclude_default_aliases
18
+ self.exclude_default_aliases = false
19
+
20
+ attr_reader :auth_context
21
+
22
+ delegate :anonymous?, :authenticated?, :metadata_manager?,
23
+ :user, :groups, :agents, :member_of?,
24
+ :authorized_to_act_as_superuser?,
25
+ to: :auth_context
26
+
27
+ def initialize(auth_context)
28
+ @auth_context = auth_context
29
+ if exclude_default_aliases
30
+ clear_aliased_actions
31
+ end
32
+ apply_ability_definitions
33
+ end
34
+
35
+ def cache
36
+ @cache ||= {}
37
+ end
38
+
39
+ def apply_ability_definitions
40
+ ability_definitions.reduce(self, :apply)
41
+ end
42
+
43
+ def apply(ability_def)
44
+ ability_def.call(self)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ module Ddr::Auth
2
+ module Affiliation
3
+
4
+ FACULTY = "faculty".freeze
5
+ STAFF = "staff".freeze
6
+ STUDENT = "student".freeze
7
+ EMERITUS = "emeritus".freeze
8
+ AFFILIATE = "affiliate".freeze
9
+ ALUMNI = "alumni".freeze
10
+
11
+ ALL = [ FACULTY, STAFF, STUDENT, EMERITUS, AFFILIATE, ALUMNI ].freeze
12
+
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module Ddr::Auth
2
+ class AffiliationGroups
3
+
4
+ all = []
5
+
6
+ Affiliation::ALL.each do |affiliation|
7
+ group = Group.new "duke.#{affiliation}",
8
+ label: "Duke #{affiliation.capitalize}" do |auth_context|
9
+ auth_context.affiliation.include? affiliation
10
+ end
11
+
12
+ const_set affiliation.upcase, group
13
+
14
+ all << const_get(affiliation.upcase)
15
+ end
16
+
17
+ ALL = all.freeze
18
+
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Ddr::Auth
2
+ class AnonymousAbility < AbstractAbility
3
+
4
+ self.ability_definitions = Ability.ability_definitions.dup
5
+
6
+ end
7
+ end
@@ -0,0 +1,109 @@
1
+ module Ddr::Auth
2
+ # @abstract
3
+ class AuthContext
4
+
5
+ attr_reader :user, :env
6
+
7
+ def initialize(user = nil, env = nil)
8
+ @user = user
9
+ @env = env
10
+ end
11
+
12
+ def ability
13
+ if anonymous?
14
+ AnonymousAbility.new(self)
15
+ elsif superuser?
16
+ SuperuserAbility.new(self)
17
+ else
18
+ default_ability_class.new(self)
19
+ end
20
+ end
21
+
22
+ def default_ability_class
23
+ Ddr::Auth::default_ability.constantize
24
+ end
25
+
26
+ # Return whether a user is absent from the auth context.
27
+ # @return [Boolean]
28
+ def anonymous?
29
+ user.nil?
30
+ end
31
+
32
+ # Return whether a user is present in the auth context.
33
+ # @return [Boolean]
34
+ def authenticated?
35
+ !anonymous?
36
+ end
37
+
38
+ # Return whether context is authenticated in superuser scope.
39
+ # @return [Boolean]
40
+ def superuser?
41
+ env && env.key?("warden") && env["warden"].authenticate?(scope: :superuser)
42
+ end
43
+
44
+ def metadata_manager?
45
+ member_of? Ddr::Auth.metadata_managers_group
46
+ end
47
+
48
+ # Return the user agent for this context.
49
+ # @return [String] or nil, if auth context is anonymous/
50
+ def agent
51
+ anonymous? ? nil : user.agent
52
+ end
53
+
54
+ # Is the authenticated agent a Duke identity?
55
+ # @return [Boolean]
56
+ def duke_agent?
57
+ !!(agent =~ /@duke\.edu\z/)
58
+ end
59
+
60
+ # Return the list of groups for this context.
61
+ # @return [Array<Group>]
62
+ def groups
63
+ @groups ||= Groups.call(self)
64
+ end
65
+
66
+ # Is the user associated with the auth context a member of the group?
67
+ # @param group [Group, String] group object or group id
68
+ # @return [Boolean]
69
+ def member_of?(group)
70
+ if group.is_a? Group
71
+ groups.include? group
72
+ else
73
+ member_of? Group.new(group)
74
+ end
75
+ end
76
+
77
+ # Is the auth context authorized to act as superuser?
78
+ # This is separate from whether the context is authenticated in superuser scope.
79
+ # @return [Boolean]
80
+ def authorized_to_act_as_superuser?
81
+ member_of? Ddr::Auth.superuser_group
82
+ end
83
+
84
+ # Return the combined user and group agents for this context.
85
+ # @return [Array<String>]
86
+ def agents
87
+ groups.map(&:agent).push(agent).compact
88
+ end
89
+
90
+ # The IP address associated with the context.
91
+ # @return [String]
92
+ def ip_address
93
+ nil
94
+ end
95
+
96
+ # The affiliation values associated with the context.
97
+ # @return [Array<String>]
98
+ def affiliation
99
+ []
100
+ end
101
+
102
+ # The remote group values associated with the context.
103
+ # @return [Array<String>]
104
+ def ismemberof
105
+ []
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,13 @@
1
+ module Ddr::Auth
2
+ class AuthContextFactory
3
+
4
+ def self.call(user = nil, env = nil)
5
+ if env
6
+ WebAuthContext.new(user, env)
7
+ else
8
+ DetachedAuthContext.new(user)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Ddr::Auth
2
+ class DetachedAuthContext < AuthContext
3
+
4
+ def affiliation
5
+ anonymous? ? super : ldap_result.affiliation
6
+ end
7
+
8
+ def ismemberof
9
+ anonymous? ? super : ldap_result.ismemberof
10
+ end
11
+
12
+ private
13
+
14
+ def ldap_result
15
+ @ldap_result ||= Ddr::Auth.ldap_gateway.find(user.user_key)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Ddr::Auth
2
+ class DynamicGroups
3
+
4
+ ALL = ([Groups::PUBLIC, Groups::REGISTERED, Groups::DUKE_ALL] + AffiliationGroups::ALL).freeze
5
+
6
+ # @param auth_context [AuthContext]
7
+ # @return [Array<Group>]
8
+ def self.call(auth_context)
9
+ ALL.select { |group| group.has_member?(auth_context) }
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Ddr::Auth
2
+ class EffectivePermissions
3
+
4
+ # @param obj [Object] an object that receives :roles and returns a RoleSet
5
+ # @param agents [String, Array<String>] agent(s) to match roles
6
+ # @return [Array<Symbol>]
7
+ def self.call(obj, agents)
8
+ EffectiveRoles.call(obj, agents).map(&:permissions).flatten.uniq
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Ddr::Auth
2
+ class EffectiveRoles
3
+
4
+ def self.call(obj, agents)
5
+ ( obj.resource_roles | obj.inherited_roles ).select { |r| agents.include?(r.agent) }
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module Ddr
2
+ module Auth
3
+ class FailureApp < Devise::FailureApp
4
+
5
+ def respond
6
+ if scope == :user && Ddr::Auth.require_shib_user_authn
7
+ store_location!
8
+ redirect_to user_omniauth_authorize_path(:shibboleth)
9
+ else
10
+ super
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ require "delegate"
2
+
3
+ module Ddr
4
+ module Auth
5
+ # Wraps a String
6
+ class Group < SimpleDelegator
7
+
8
+ attr_reader :rule
9
+
10
+ def initialize(id, opts={}, &rule)
11
+ super(id)
12
+ @label = opts[:label]
13
+ @rule = rule
14
+ freeze
15
+ end
16
+
17
+ # @param user [Ddr::Auth::AuthContext]
18
+ def has_member?(auth_context)
19
+ rule ? instance_exec(auth_context, &rule) : auth_context.member_of?(self)
20
+ end
21
+
22
+ def id
23
+ __getobj__
24
+ end
25
+
26
+ def label
27
+ @label || id
28
+ end
29
+
30
+ def agent
31
+ to_s
32
+ end
33
+
34
+ def inspect
35
+ "#<#{self.class.name} id=#{id.inspect}, label=#{label.inspect}>"
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,70 @@
1
+ require 'grouper-rest-client'
2
+ require "delegate"
3
+
4
+ module Ddr
5
+ module Auth
6
+ class GrouperGateway < SimpleDelegator
7
+
8
+ SUBJECT_ID_RE = Regexp.new('[^@]+(?=@duke\.edu)')
9
+ DEFAULT_TIMEOUT = 5
10
+
11
+ def self.repository_groups(*args)
12
+ new.repository_groups(*args)
13
+ end
14
+
15
+ def self.user_groups(*args)
16
+ new.user_groups(*args)
17
+ end
18
+
19
+ def initialize
20
+ super Grouper::Rest::Client::Resource.new(ENV["GROUPER_URL"],
21
+ user: ENV["GROUPER_USER"],
22
+ password: ENV["GROUPER_PASSWORD"],
23
+ timeout: ENV.fetch("GROUPER_TIMEOUT", DEFAULT_TIMEOUT).to_i)
24
+ end
25
+
26
+ # List of all grouper groups for the repository
27
+ def repository_groups(raw = false)
28
+ repo_groups = groups(REPOSITORY_GROUP_FILTER)
29
+ if ok?
30
+ return repo_groups if raw
31
+ repo_groups.map do |g|
32
+ Group.new(g["name"], label: g["displayExtension"])
33
+ end
34
+ else
35
+ []
36
+ end
37
+ end
38
+
39
+ def user_groups(user, raw = false)
40
+ groups = []
41
+ subject_id = user.principal_name.scan(SUBJECT_ID_RE).first
42
+ return groups unless subject_id
43
+ begin
44
+ request_body = {
45
+ "WsRestGetGroupsRequest" => {
46
+ "subjectLookups" => [{"subjectIdentifier" => subject_id}]
47
+ }
48
+ }
49
+ # Have to use :call b/c grouper-rest-client :subjects method doesn't support POST
50
+ response = call("subjects", :post, request_body)
51
+ if ok?
52
+ result = response["WsGetGroupsResults"]["results"].first
53
+ # Have to manually filter results b/c Grouper WS version 1.5 does not support filter parameter
54
+ if result && result["wsGroups"]
55
+ groups = result["wsGroups"].select { |g| g["name"] =~ /^#{REPOSITORY_GROUP_FILTER}/ }
56
+ end
57
+ end
58
+ rescue StandardError => e
59
+ # XXX Should we raise a custom exception?
60
+ Rails.logger.error e
61
+ end
62
+ return groups if raw
63
+ groups.map do |g|
64
+ Group.new(g["name"], label: g["displayExtension"])
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,32 @@
1
+ module Ddr
2
+ module Auth
3
+ module Groups
4
+
5
+ PUBLIC = Group.new "public", label: "Public" do |auth_context|
6
+ true
7
+ end
8
+
9
+ REGISTERED = Group.new "registered", label: "Registered Users" do |auth_context|
10
+ auth_context.authenticated?
11
+ end
12
+
13
+ DUKE_ALL = Group.new "duke.all", label: "Duke NetIDs" do |auth_context|
14
+ auth_context.duke_agent?
15
+ end
16
+
17
+ # Return the list of all groups available for use in the repository,
18
+ # i.e., that can be used to assert access controls.
19
+ # @return [Array<Group>] the groups
20
+ def self.all
21
+ DynamicGroups::ALL + Ddr::Auth.grouper_gateway.repository_groups
22
+ end
23
+
24
+ # @param auth_context [AuthContext]
25
+ # @return [Array<Group>]
26
+ def self.call(auth_context)
27
+ DynamicGroups.call(auth_context) + RemoteGroups.call(auth_context)
28
+ end
29
+
30
+ end
31
+ end
32
+ end