ddr-core 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +12 -0
- data/README.md +27 -0
- data/Rakefile +30 -0
- data/app/assets/config/ddr_core_manifest.js +0 -0
- data/app/controllers/users/omniauth_callbacks_controller.rb +11 -0
- data/app/controllers/users/sessions_controller.rb +15 -0
- data/app/models/concerns/ddr/captionable.rb +25 -0
- data/app/models/concerns/ddr/describable.rb +108 -0
- data/app/models/concerns/ddr/governable.rb +25 -0
- data/app/models/concerns/ddr/has_admin_metadata.rb +141 -0
- data/app/models/concerns/ddr/has_attachments.rb +10 -0
- data/app/models/concerns/ddr/has_children.rb +10 -0
- data/app/models/concerns/ddr/has_content.rb +132 -0
- data/app/models/concerns/ddr/has_extracted_text.rb +10 -0
- data/app/models/concerns/ddr/has_intermediate_file.rb +25 -0
- data/app/models/concerns/ddr/has_multires_image.rb +14 -0
- data/app/models/concerns/ddr/has_parent.rb +18 -0
- data/app/models/concerns/ddr/has_struct_metadata.rb +21 -0
- data/app/models/concerns/ddr/has_thumbnail.rb +33 -0
- data/app/models/concerns/ddr/solr_document_behavior.rb +429 -0
- data/app/models/concerns/ddr/streamable.rb +25 -0
- data/app/models/ddr/admin_set.rb +28 -0
- data/app/models/ddr/attachment.rb +14 -0
- data/app/models/ddr/collection.rb +28 -0
- data/app/models/ddr/component.rb +31 -0
- data/app/models/ddr/contact.rb +23 -0
- data/app/models/ddr/digest.rb +8 -0
- data/app/models/ddr/file.rb +40 -0
- data/app/models/ddr/item.rb +36 -0
- data/app/models/ddr/language.rb +31 -0
- data/app/models/ddr/media_type.rb +22 -0
- data/app/models/ddr/resource.rb +94 -0
- data/app/models/ddr/rights_statement.rb +25 -0
- data/app/models/ddr/target.rb +17 -0
- data/config/initializers/devise.rb +262 -0
- data/config/locales/ddr-core.en.yml +85 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20141104181418_create_users.rb +34 -0
- data/db/migrate/20141107124012_add_columns_to_user.rb +46 -0
- data/lib/ddr-core.rb +1 -0
- data/lib/ddr/auth.rb +80 -0
- data/lib/ddr/auth/ability.rb +18 -0
- data/lib/ddr/auth/ability_definitions.rb +26 -0
- data/lib/ddr/auth/ability_definitions/admin_set_ability_definitions.rb +9 -0
- data/lib/ddr/auth/ability_definitions/alias_ability_definitions.rb +23 -0
- data/lib/ddr/auth/ability_definitions/attachment_ability_definitions.rb +13 -0
- data/lib/ddr/auth/ability_definitions/collection_ability_definitions.rb +28 -0
- data/lib/ddr/auth/ability_definitions/component_ability_definitions.rb +13 -0
- data/lib/ddr/auth/ability_definitions/item_ability_definitions.rb +13 -0
- data/lib/ddr/auth/ability_definitions/lock_ability_definitions.rb +13 -0
- data/lib/ddr/auth/ability_definitions/publication_ability_definitions.rb +16 -0
- data/lib/ddr/auth/ability_definitions/role_based_ability_definitions.rb +39 -0
- data/lib/ddr/auth/ability_definitions/superuser_ability_definitions.rb +9 -0
- data/lib/ddr/auth/ability_factory.rb +10 -0
- data/lib/ddr/auth/abstract_ability.rb +48 -0
- data/lib/ddr/auth/affiliation.rb +14 -0
- data/lib/ddr/auth/affiliation_groups.rb +20 -0
- data/lib/ddr/auth/anonymous_ability.rb +7 -0
- data/lib/ddr/auth/auth_context.rb +109 -0
- data/lib/ddr/auth/auth_context_factory.rb +13 -0
- data/lib/ddr/auth/detached_auth_context.rb +19 -0
- data/lib/ddr/auth/dynamic_groups.rb +13 -0
- data/lib/ddr/auth/effective_permissions.rb +12 -0
- data/lib/ddr/auth/effective_roles.rb +9 -0
- data/lib/ddr/auth/failure_app.rb +16 -0
- data/lib/ddr/auth/group.rb +40 -0
- data/lib/ddr/auth/grouper_gateway.rb +70 -0
- data/lib/ddr/auth/groups.rb +32 -0
- data/lib/ddr/auth/ldap_gateway.rb +74 -0
- data/lib/ddr/auth/permissions.rb +18 -0
- data/lib/ddr/auth/remote_groups.rb +14 -0
- data/lib/ddr/auth/role_based_access_controls_enforcement.rb +56 -0
- data/lib/ddr/auth/roles.rb +28 -0
- data/lib/ddr/auth/roles/role.rb +121 -0
- data/lib/ddr/auth/roles/role_type.rb +23 -0
- data/lib/ddr/auth/roles/role_types.rb +52 -0
- data/lib/ddr/auth/superuser_ability.rb +7 -0
- data/lib/ddr/auth/test_helpers.rb +22 -0
- data/lib/ddr/auth/user.rb +54 -0
- data/lib/ddr/auth/web_auth_context.rb +29 -0
- data/lib/ddr/core.rb +110 -0
- data/lib/ddr/core/engine.rb +8 -0
- data/lib/ddr/core/version.rb +5 -0
- data/lib/ddr/error.rb +16 -0
- data/lib/ddr/files.rb +13 -0
- data/lib/ddr/fits.rb +189 -0
- data/lib/ddr/index.rb +29 -0
- data/lib/ddr/index/abstract_query_result.rb +22 -0
- data/lib/ddr/index/connection.rb +38 -0
- data/lib/ddr/index/csv_query_result.rb +84 -0
- data/lib/ddr/index/document_builder.rb +9 -0
- data/lib/ddr/index/field.rb +35 -0
- data/lib/ddr/index/field_attribute.rb +22 -0
- data/lib/ddr/index/fields.rb +154 -0
- data/lib/ddr/index/filter.rb +139 -0
- data/lib/ddr/index/query.rb +82 -0
- data/lib/ddr/index/query_builder.rb +185 -0
- data/lib/ddr/index/query_clause.rb +112 -0
- data/lib/ddr/index/query_params.rb +40 -0
- data/lib/ddr/index/query_result.rb +102 -0
- data/lib/ddr/index/response.rb +30 -0
- data/lib/ddr/index/sort_order.rb +28 -0
- data/lib/ddr/index/unique_key_field.rb +12 -0
- data/lib/ddr/managers.rb +9 -0
- data/lib/ddr/managers/manager.rb +13 -0
- data/lib/ddr/managers/technical_metadata_manager.rb +141 -0
- data/lib/ddr/structure.rb +188 -0
- data/lib/ddr/structures/agent.rb +49 -0
- data/lib/ddr/structures/component_type_term.rb +29 -0
- data/lib/ddr/structures/div.rb +64 -0
- data/lib/ddr/structures/f_locat.rb +54 -0
- data/lib/ddr/structures/file.rb +52 -0
- data/lib/ddr/structures/file_grp.rb +35 -0
- data/lib/ddr/structures/file_sec.rb +22 -0
- data/lib/ddr/structures/fptr.rb +31 -0
- data/lib/ddr/structures/mets_hdr.rb +37 -0
- data/lib/ddr/structures/mptr.rb +49 -0
- data/lib/ddr/structures/struct_map.rb +40 -0
- data/lib/ddr/utils.rb +185 -0
- data/lib/ddr/vocab.rb +22 -0
- data/lib/ddr/vocab/asset.rb +51 -0
- data/lib/ddr/vocab/contact.rb +9 -0
- data/lib/ddr/vocab/display.rb +9 -0
- data/lib/ddr/vocab/duke_terms.rb +13 -0
- data/lib/ddr/vocab/rdf_vocabulary_parser.rb +43 -0
- data/lib/ddr/vocab/roles.rb +25 -0
- data/lib/ddr/vocab/sources/duketerms.rdf +870 -0
- data/lib/ddr/vocab/vocabulary.rb +37 -0
- data/lib/ddr/workflow.rb +8 -0
- data/lib/tasks/ddr/core_tasks.rake +4 -0
- metadata +428 -0
@@ -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,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,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,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
|