ddr-core 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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