hydra-access-controls 10.3.4 → 10.4.0.rc1
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.
- checksums.yaml +4 -4
- data/app/models/concerns/hydra/access_controls/permissions.rb +2 -2
- data/app/models/hydra/access_control.rb +13 -1
- data/lib/hydra/role_mapper_behavior.rb +21 -10
- data/lib/hydra/user.rb +9 -8
- data/spec/factories.rb +0 -12
- data/spec/support/user.rb +3 -21
- data/spec/unit/ability_spec.rb +27 -46
- data/spec/unit/accessible_by_spec.rb +1 -1
- data/spec/unit/admin_policy_spec.rb +6 -5
- data/spec/unit/permission_spec.rb +2 -2
- data/spec/unit/permissions_spec.rb +2 -1
- data/spec/unit/policy_aware_ability_spec.rb +24 -20
- data/spec/unit/policy_aware_access_controls_enforcement_spec.rb +23 -15
- data/spec/unit/role_mapper_spec.rb +53 -19
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81f71a1cc06b6a7534f660864351689d42bcf25f
|
4
|
+
data.tar.gz: b5cc13c664f4bfdd5ca77d3033bbfdceb5106dd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b6074c2e8ce4c4db2509118062c76711935285192bcc2c767a8b0fe9b7bc16bec77a1e81836d66585e16210ff3eb855d4a3bde015cc989854fa33db5ac1aa6c
|
7
|
+
data.tar.gz: 138f70c4e3a0cd8f82cf09dfcd31fa9e0a11807971bc58b4e6445d0229d2e062198648300bb546de5e25cb29a991571fbb76e38e0bae964088918f0a3e140ee8
|
@@ -415,10 +415,10 @@ module Hydra
|
|
415
415
|
|
416
416
|
# @param [RDF::URI] mode One of the permissions modes, e.g. ACL.Write, ACL.Read, etc.
|
417
417
|
# @yieldparam [Array<ActiveFedora::Base>] agent the agent type assertions
|
418
|
-
# @return [Array<Permission>] list of permissions where the mode is as selected, the block evaluates to true
|
418
|
+
# @return [Array<Permission>] list of permissions where the mode is as selected, the block evaluates to true
|
419
419
|
def search_by_mode(mode)
|
420
420
|
permissions.to_a.select do |p|
|
421
|
-
yield(p.agent) &&
|
421
|
+
yield(p.agent) && p.mode.first.rdf_subject == mode
|
422
422
|
end
|
423
423
|
end
|
424
424
|
|
@@ -20,11 +20,13 @@ module Hydra
|
|
20
20
|
|
21
21
|
def permissions_attributes=(attribute_list)
|
22
22
|
raise ArgumentError unless attribute_list.is_a? Array
|
23
|
+
any_destroyed = false
|
23
24
|
attribute_list.each do |attributes|
|
24
25
|
if attributes.key?(:id)
|
25
26
|
obj = relationship.find(attributes[:id])
|
26
27
|
if has_destroy_flag?(attributes)
|
27
28
|
obj.destroy
|
29
|
+
any_destroyed = true
|
28
30
|
else
|
29
31
|
obj.update(attributes.except(:id, '_destroy'))
|
30
32
|
end
|
@@ -32,18 +34,28 @@ module Hydra
|
|
32
34
|
relationship.create(attributes)
|
33
35
|
end
|
34
36
|
end
|
37
|
+
# Poison the cache
|
38
|
+
relationship.reset if any_destroyed
|
35
39
|
end
|
36
40
|
|
37
41
|
def relationship
|
38
42
|
@relationship ||= CollectionRelationship.new(self, :contains)
|
39
43
|
end
|
40
44
|
|
45
|
+
# This is like a has_many :through relationship
|
41
46
|
class CollectionRelationship
|
42
47
|
def initialize(owner, reflection)
|
43
48
|
@owner = owner
|
44
49
|
@relationship = @owner.send(reflection)
|
45
50
|
end
|
46
51
|
|
52
|
+
# The graph stored in @owner is now stale, so reload it and clear all caches
|
53
|
+
def reset
|
54
|
+
@owner.reload
|
55
|
+
@relationship.proxy_association.reload
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
47
59
|
delegate :to_a, :to_ary, :map, :delete, :first, :last, :size, :count, :[],
|
48
60
|
:==, :detect, :empty?, :each, :any?, :all?, :include?, :destroy_all,
|
49
61
|
to: :@relationship
|
@@ -52,7 +64,7 @@ module Hydra
|
|
52
64
|
# delegate find.
|
53
65
|
def find(id)
|
54
66
|
return to_a.find { |record| record.id == id } if @relationship.loaded?
|
55
|
-
|
67
|
+
|
56
68
|
unless id.start_with?(@owner.id)
|
57
69
|
raise ArgumentError, "requested ACL (#{id}) is not a member of #{@owner.id}"
|
58
70
|
end
|
@@ -1,25 +1,28 @@
|
|
1
1
|
module Hydra::RoleMapperBehavior
|
2
2
|
extend ActiveSupport::Concern
|
3
|
+
extend Deprecation
|
3
4
|
|
4
5
|
module ClassMethods
|
5
6
|
def role_names
|
6
7
|
map.keys
|
7
8
|
end
|
8
9
|
|
10
|
+
def fetch_groups(user:)
|
11
|
+
_groups(user.user_key)
|
12
|
+
end
|
13
|
+
|
9
14
|
##
|
10
15
|
# @param user_or_uid either the User object or user id
|
11
16
|
# If you pass in a nil User object (ie. user isn't logged in), or a uid that doesn't exist, it will return an empty array
|
12
17
|
def roles(user_or_uid)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
array = array << 'registered' unless (user.nil? || user.new_record?)
|
22
|
-
array
|
18
|
+
Deprecation.warn(self, "roles is deprecated and will be removed in Hydra-Head 11. Use fetch_groups instead")
|
19
|
+
user_id = case user_or_uid
|
20
|
+
when String
|
21
|
+
user_or_uid
|
22
|
+
else
|
23
|
+
user_or_uid.user_key
|
24
|
+
end
|
25
|
+
_groups(user_id)
|
23
26
|
end
|
24
27
|
|
25
28
|
def whois(r)
|
@@ -38,6 +41,14 @@ module Hydra::RoleMapperBehavior
|
|
38
41
|
end
|
39
42
|
|
40
43
|
private
|
44
|
+
|
45
|
+
##
|
46
|
+
# @param user_id [String] the identfying user key
|
47
|
+
# @return [Array<String>] a list of group names. If a nil user id, or a user id that doesn't exist is passed in, it will return an empty array
|
48
|
+
def _groups(user_id)
|
49
|
+
byname[user_id].dup || []
|
50
|
+
end
|
51
|
+
|
41
52
|
def load_role_map
|
42
53
|
require 'erb'
|
43
54
|
require 'yaml'
|
data/lib/hydra/user.rb
CHANGED
@@ -2,23 +2,24 @@
|
|
2
2
|
# By default, this module assumes you are using the User model created by Blacklight, which uses Devise.
|
3
3
|
# To integrate your own User implementation into Hydra, override this Module or define your own User model in app/models/user.rb within your Hydra head.
|
4
4
|
module Hydra::User
|
5
|
+
extend ActiveSupport::Concern
|
5
6
|
include Blacklight::AccessControls::User
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
included do
|
9
|
+
class_attribute :group_service
|
10
|
+
self.group_service = RoleMapper
|
10
11
|
end
|
11
12
|
|
12
13
|
def groups
|
13
|
-
|
14
|
+
group_service.fetch_groups(user: self)
|
14
15
|
end
|
15
16
|
|
16
17
|
module ClassMethods
|
17
|
-
# This method
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# This method finds User objects using the user_key as specified by the
|
19
|
+
# Devise authentication_keys configuration variable. This method encapsulates
|
20
|
+
# whether we use email or username (or something else) as the identifing user attribute.
|
20
21
|
def find_by_user_key(key)
|
21
|
-
|
22
|
+
find_by(Devise.authentication_keys.first.to_sym => key)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
data/spec/factories.rb
CHANGED
@@ -7,9 +7,7 @@ FactoryGirl.define do
|
|
7
7
|
sequence :uid do |n|
|
8
8
|
"person#{n}"
|
9
9
|
end
|
10
|
-
email { "#{uid}@example.com" }
|
11
10
|
password { uid }
|
12
|
-
new_record false
|
13
11
|
end
|
14
12
|
|
15
13
|
factory :archivist, :parent=>:user do |u|
|
@@ -23,52 +21,42 @@ FactoryGirl.define do
|
|
23
21
|
factory :staff, :parent=>:user do |u|
|
24
22
|
uid 'staff1'
|
25
23
|
password 'staff1'
|
26
|
-
roles { ["staff"] }
|
27
24
|
end
|
28
25
|
factory :student, :parent=>:user do |u|
|
29
26
|
uid 'student1'
|
30
27
|
password 'student1'
|
31
|
-
roles { ["student"] }
|
32
28
|
end
|
33
29
|
factory :joe_creator, :parent=>:user do |u|
|
34
30
|
uid 'joe_creator'
|
35
31
|
password 'joe_creator'
|
36
|
-
roles { ["faculty"] }
|
37
32
|
end
|
38
33
|
factory :martia_morocco, :parent=>:user do |u|
|
39
34
|
uid 'martia_morocco'
|
40
35
|
password 'martia_morocco'
|
41
|
-
roles { ["faculty", "africana-faculty"] }
|
42
36
|
end
|
43
37
|
factory :ira_instructor, :parent=>:user do |u|
|
44
38
|
uid 'ira_instructor'
|
45
39
|
password 'ira_instructor'
|
46
|
-
roles { ["faculty", "africana-faculty"] }
|
47
40
|
end
|
48
41
|
factory :calvin_collaborator, :parent=>:user do |u|
|
49
42
|
uid 'calvin_collaborator'
|
50
43
|
password 'calvin_collaborator'
|
51
|
-
roles { ["student"] }
|
52
44
|
end
|
53
45
|
factory :sara_student, :parent=>:user do |u|
|
54
46
|
uid 'sara_student'
|
55
47
|
password 'sara_student'
|
56
|
-
roles { ["student", "africana-104-students"] }
|
57
48
|
end
|
58
49
|
factory :louis_librarian, :parent=>:user do |u|
|
59
50
|
uid 'louis_librarian'
|
60
51
|
password 'louis_librarian'
|
61
|
-
roles { ["library-staff", "repository-admin"] }
|
62
52
|
end
|
63
53
|
factory :carol_curator, :parent=>:user do |u|
|
64
54
|
uid 'carol_curator'
|
65
55
|
password 'carol_curator'
|
66
|
-
roles { ["library-staff", "repository-admin"] }
|
67
56
|
end
|
68
57
|
factory :alice_admin, :parent=>:user do |u|
|
69
58
|
uid 'alice_admin'
|
70
59
|
password 'alice_admin'
|
71
|
-
roles { ["repository-admin"] }
|
72
60
|
end
|
73
61
|
|
74
62
|
#
|
data/spec/support/user.rb
CHANGED
@@ -2,29 +2,11 @@ class User
|
|
2
2
|
|
3
3
|
include Hydra::User
|
4
4
|
|
5
|
-
attr_accessor :uid
|
5
|
+
attr_accessor :uid
|
6
6
|
|
7
7
|
def initialize(params={})
|
8
|
-
self.
|
9
|
-
|
10
|
-
self.new_record = params[:new_record] if params[:new_record]
|
11
|
-
end
|
12
|
-
|
13
|
-
def new_record?
|
14
|
-
new_record == true
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.find_by_uid(uid)
|
18
|
-
nil
|
19
|
-
end
|
20
|
-
|
21
|
-
def save
|
22
|
-
# do nothing!
|
23
|
-
end
|
24
|
-
|
25
|
-
def save!
|
26
|
-
save
|
27
|
-
return self
|
8
|
+
self.uid = params.delete(:uid) if params[:uid]
|
9
|
+
super
|
28
10
|
end
|
29
11
|
|
30
12
|
end
|
data/spec/unit/ability_spec.rb
CHANGED
@@ -15,13 +15,15 @@ describe Ability do
|
|
15
15
|
its(:discover_user_field) { should == 'discover_access_person_ssim'}
|
16
16
|
end
|
17
17
|
|
18
|
+
subject { Ability.new(user) }
|
19
|
+
|
18
20
|
context "for a not-signed in user" do
|
19
21
|
before do
|
20
22
|
allow_any_instance_of(User).to receive(:email).and_return(nil)
|
21
23
|
allow_any_instance_of(User).to receive(:new_record?).and_return(true)
|
22
24
|
end
|
23
|
-
|
24
|
-
it "
|
25
|
+
let(:user) { nil }
|
26
|
+
it "calls custom_permissions" do
|
25
27
|
expect_any_instance_of(Ability).to receive(:custom_permissions)
|
26
28
|
subject.can?(:delete, 7)
|
27
29
|
end
|
@@ -29,10 +31,7 @@ describe Ability do
|
|
29
31
|
end
|
30
32
|
|
31
33
|
context "for a signed in user" do
|
32
|
-
|
33
|
-
@user = FactoryGirl.build(:registered_user)
|
34
|
-
end
|
35
|
-
subject { Ability.new(@user) }
|
34
|
+
let(:user) { FactoryGirl.build(:registered_user) }
|
36
35
|
|
37
36
|
it { should_not be_able_to(:create, ActiveFedora::Base) }
|
38
37
|
end
|
@@ -50,7 +49,7 @@ describe Ability do
|
|
50
49
|
end
|
51
50
|
|
52
51
|
context "Then a not-signed-in user" do
|
53
|
-
|
52
|
+
let(:user) { nil }
|
54
53
|
it { should be_able_to(:discover, asset) }
|
55
54
|
it { should_not be_able_to(:read, asset) }
|
56
55
|
it { should_not be_able_to(:edit, asset) }
|
@@ -59,10 +58,7 @@ describe Ability do
|
|
59
58
|
end
|
60
59
|
|
61
60
|
context "Then a registered user" do
|
62
|
-
|
63
|
-
@user = FactoryGirl.build(:registered_user)
|
64
|
-
end
|
65
|
-
subject { Ability.new(@user) }
|
61
|
+
let(:user) { FactoryGirl.build(:registered_user) }
|
66
62
|
it { should be_able_to(:discover, asset) }
|
67
63
|
it { should_not be_able_to(:read, asset) }
|
68
64
|
it { should_not be_able_to(:edit, asset) }
|
@@ -80,7 +76,7 @@ describe Ability do
|
|
80
76
|
end
|
81
77
|
|
82
78
|
context "Then a not-signed-in user" do
|
83
|
-
|
79
|
+
let(:user) { nil }
|
84
80
|
it { should be_able_to(:discover, asset) }
|
85
81
|
it { should be_able_to(:read, asset) }
|
86
82
|
it { should_not be_able_to(:edit, asset) }
|
@@ -89,10 +85,7 @@ describe Ability do
|
|
89
85
|
end
|
90
86
|
|
91
87
|
context "Then a registered user" do
|
92
|
-
|
93
|
-
@user = FactoryGirl.build(:registered_user)
|
94
|
-
end
|
95
|
-
subject { Ability.new(@user) }
|
88
|
+
let(:user) { FactoryGirl.build(:registered_user) }
|
96
89
|
it { should be_able_to(:discover, asset) }
|
97
90
|
it { should be_able_to(:read, asset) }
|
98
91
|
it { should_not be_able_to(:edit, asset) }
|
@@ -102,7 +95,6 @@ describe Ability do
|
|
102
95
|
end
|
103
96
|
|
104
97
|
describe "Given an asset with no custom access set" do
|
105
|
-
#let(:asset) { FactoryGirl.create(:default_access_asset) }
|
106
98
|
let(:asset) { FactoryGirl.create(:asset) }
|
107
99
|
before do
|
108
100
|
asset.permissions_attributes = [{ name: "joe_creator", access: "edit", type: "person" }]
|
@@ -110,8 +102,7 @@ describe Ability do
|
|
110
102
|
end
|
111
103
|
let(:solr_doc) { SolrDocument.new(asset.to_solr.merge(id: asset.id)) }
|
112
104
|
context "Then a not-signed-in user" do
|
113
|
-
let(:user) { User.new
|
114
|
-
subject { Ability.new(user) }
|
105
|
+
let(:user) { User.new }
|
115
106
|
it { should_not be_able_to(:discover, asset) }
|
116
107
|
it { should_not be_able_to(:read, asset) }
|
117
108
|
it { should_not be_able_to(:edit, asset) }
|
@@ -119,7 +110,7 @@ describe Ability do
|
|
119
110
|
it { should_not be_able_to(:destroy, asset) }
|
120
111
|
end
|
121
112
|
context "Then a registered user" do
|
122
|
-
|
113
|
+
let(:user) { FactoryGirl.build(:registered_user) }
|
123
114
|
it { should_not be_able_to(:discover, asset) }
|
124
115
|
it { should_not be_able_to(:read, asset) }
|
125
116
|
it { should_not be_able_to(:edit, asset) }
|
@@ -127,7 +118,7 @@ describe Ability do
|
|
127
118
|
it { should_not be_able_to(:destroy, asset) }
|
128
119
|
end
|
129
120
|
context "Then the Creator" do
|
130
|
-
|
121
|
+
let(:user) { FactoryGirl.build(:joe_creator) }
|
131
122
|
it { should be_able_to(:discover, asset) }
|
132
123
|
it { should be_able_to(:read, asset) }
|
133
124
|
it { should be_able_to(:edit, asset) }
|
@@ -148,10 +139,10 @@ describe Ability do
|
|
148
139
|
asset.save
|
149
140
|
end
|
150
141
|
context "The a registered user" do
|
142
|
+
let(:user) { FactoryGirl.build(:registered_user) }
|
151
143
|
before do
|
152
|
-
|
144
|
+
allow(user).to receive(:new_record?).and_return(false)
|
153
145
|
end
|
154
|
-
subject { Ability.new(@user) }
|
155
146
|
|
156
147
|
it { should be_able_to(:discover, asset) }
|
157
148
|
it { should be_able_to(:read, asset) }
|
@@ -163,18 +154,15 @@ describe Ability do
|
|
163
154
|
end
|
164
155
|
|
165
156
|
describe "Given an asset with collaborator" do
|
166
|
-
# let(:asset) { FactoryGirl.create(:group_edit_asset) }
|
167
157
|
let(:asset) { FactoryGirl.create(:asset) }
|
168
158
|
before do
|
169
159
|
asset.permissions_attributes = [{ name:"africana-faculty", access: "edit", type: "group" }, {name: "calvin_collaborator", access: "edit", type: "person"}]
|
170
160
|
asset.save
|
171
161
|
end
|
172
162
|
after { asset.destroy }
|
163
|
+
|
173
164
|
context "Then a collaborator with edit access (user permision)" do
|
174
|
-
|
175
|
-
@user = FactoryGirl.build(:calvin_collaborator)
|
176
|
-
end
|
177
|
-
subject { Ability.new(@user) }
|
165
|
+
let(:user) { FactoryGirl.build(:calvin_collaborator) }
|
178
166
|
|
179
167
|
it { should be_able_to(:discover, asset) }
|
180
168
|
it { should be_able_to(:read, asset) }
|
@@ -185,13 +173,12 @@ describe Ability do
|
|
185
173
|
end
|
186
174
|
|
187
175
|
context "Then a collaborator with edit access (group permision)" do
|
176
|
+
let(:user) { FactoryGirl.build(:martia_morocco) }
|
188
177
|
before do
|
189
|
-
|
190
|
-
allow(RoleMapper).to receive(:roles).with(@user).and_return(@user.roles)
|
178
|
+
allow(user).to receive(:groups).and_return(["faculty", "africana-faculty"])
|
191
179
|
end
|
192
|
-
subject { Ability.new(@user) }
|
193
180
|
|
194
|
-
it { should
|
181
|
+
it { should be_able_to(:read, asset) }
|
195
182
|
end
|
196
183
|
end
|
197
184
|
|
@@ -203,10 +190,7 @@ describe Ability do
|
|
203
190
|
asset.save
|
204
191
|
end
|
205
192
|
context "Then a registered user" do
|
206
|
-
|
207
|
-
@user = FactoryGirl.build(:registered_user)
|
208
|
-
end
|
209
|
-
subject { Ability.new(@user) }
|
193
|
+
let(:user) { FactoryGirl.build(:registered_user) }
|
210
194
|
|
211
195
|
it { should_not be_able_to(:discover, asset) }
|
212
196
|
it { should_not be_able_to(:read, asset) }
|
@@ -217,11 +201,10 @@ describe Ability do
|
|
217
201
|
end
|
218
202
|
|
219
203
|
context "Then someone whose role/group has read access" do
|
204
|
+
let(:user) { FactoryGirl.build(:martia_morocco) }
|
220
205
|
before do
|
221
|
-
|
222
|
-
allow(RoleMapper).to receive(:roles).with(@user).and_return(@user.roles)
|
206
|
+
allow(user).to receive(:groups).and_return(["faculty", "africana-faculty"])
|
223
207
|
end
|
224
|
-
subject { Ability.new(@user) }
|
225
208
|
|
226
209
|
it { should be_able_to(:discover, asset) }
|
227
210
|
it { should be_able_to(:read, asset) }
|
@@ -244,34 +227,33 @@ describe Ability do
|
|
244
227
|
can :accept, ActiveFedora::Base
|
245
228
|
end
|
246
229
|
end
|
247
|
-
@user = FactoryGirl.create(:staff)
|
248
230
|
end
|
231
|
+
let(:user) { FactoryGirl.build(:staff) }
|
249
232
|
|
250
233
|
after do
|
251
234
|
Object.send(:remove_const, :MyAbility)
|
252
235
|
end
|
253
236
|
|
254
|
-
subject { MyAbility.new(
|
237
|
+
subject { MyAbility.new(user) }
|
255
238
|
|
256
239
|
it { should be_able_to(:accept, ActiveFedora::Base) }
|
257
240
|
|
258
241
|
end
|
259
242
|
|
260
243
|
describe "calling ability on two separate objects" do
|
261
|
-
#asset1 = FactoryGirl.create(:org_read_access_asset)
|
262
244
|
let(:asset1) { FactoryGirl.create(:asset) }
|
263
245
|
let(:asset2) { FactoryGirl.create(:asset) }
|
264
246
|
before do
|
265
247
|
asset1.permissions_attributes = [{ name: "registered", access: "read", type: "group" }, { name: "joe_creator", access: "edit", type: "person" }, { name: "calvin_collaborator", access: "edit", type: "person" }]
|
266
248
|
asset1.save
|
267
|
-
@user = FactoryGirl.build(:calvin_collaborator) # has access to @asset1, but not @asset2
|
268
249
|
end
|
250
|
+
let(:user) { FactoryGirl.build(:calvin_collaborator) } # has access to @asset1, but not @asset2
|
269
251
|
after do
|
270
252
|
asset1.destroy
|
271
253
|
asset2.destroy
|
272
254
|
end
|
273
|
-
|
274
|
-
it "
|
255
|
+
|
256
|
+
it "is readable in the first instance and not in the second instance" do
|
275
257
|
# We had a bug around this where it keeps returning the access for the first object queried
|
276
258
|
expect(subject).to be_able_to(:edit, asset1)
|
277
259
|
expect(subject).to_not be_able_to(:edit, asset2)
|
@@ -279,7 +261,6 @@ describe Ability do
|
|
279
261
|
end
|
280
262
|
|
281
263
|
describe "download permissions" do
|
282
|
-
subject { Ability.new(user) }
|
283
264
|
let(:asset) { FactoryGirl.create(:asset) }
|
284
265
|
let(:user) { FactoryGirl.build(:user) }
|
285
266
|
let(:file) { ActiveFedora::File.new() }
|
@@ -14,7 +14,7 @@ describe "active_fedora/accessible_by" do
|
|
14
14
|
public_obj.save
|
15
15
|
editable_obj.permissions_attributes = [{ name:"africana-faculty", access: "edit", type: "group" }, {name: "calvin_collaborator", access: "edit", type: "person"}]
|
16
16
|
editable_obj.save
|
17
|
-
expect(user).to receive(:groups).at_most(:once).and_return(
|
17
|
+
expect(user).to receive(:groups).at_most(:once).and_return(["faculty", "africana-faculty"])
|
18
18
|
end
|
19
19
|
|
20
20
|
after do
|
@@ -120,9 +120,10 @@ describe Hydra::AdminPolicy do
|
|
120
120
|
# Policy-based Access Controls
|
121
121
|
#
|
122
122
|
describe "When accessing assets with Policies associated" do
|
123
|
+
let(:user) { FactoryGirl.build(:martia_morocco) }
|
124
|
+
|
123
125
|
before do
|
124
|
-
|
125
|
-
allow(RoleMapper).to receive(:roles).with(@user).and_return(@user.roles)
|
126
|
+
allow(user).to receive(:groups).and_return(["faculty", "africana-faculty"])
|
126
127
|
end
|
127
128
|
|
128
129
|
before(:all) do
|
@@ -135,7 +136,7 @@ describe Hydra::AdminPolicy do
|
|
135
136
|
Object.send(:remove_const, :TestAbility)
|
136
137
|
end
|
137
138
|
|
138
|
-
subject { TestAbility.new(
|
139
|
+
subject { TestAbility.new(user) }
|
139
140
|
|
140
141
|
context "Given a policy grants read access to a group I belong to" do
|
141
142
|
before do
|
@@ -191,7 +192,7 @@ describe Hydra::AdminPolicy do
|
|
191
192
|
context "And a subscribing asset grants read access to me as an individual" do
|
192
193
|
before do
|
193
194
|
@asset = ModsAsset.new()
|
194
|
-
@asset.read_users = [
|
195
|
+
@asset.read_users = [user.uid]
|
195
196
|
@asset.admin_policy = @policy
|
196
197
|
@asset.save
|
197
198
|
end
|
@@ -235,7 +236,7 @@ describe Hydra::AdminPolicy do
|
|
235
236
|
context "And a subscribing asset grants read access to me as an individual" do
|
236
237
|
before do
|
237
238
|
@asset = ModsAsset.new()
|
238
|
-
@asset.read_users = [
|
239
|
+
@asset.read_users = [user.uid]
|
239
240
|
@asset.admin_policy = @policy
|
240
241
|
@asset.save
|
241
242
|
end
|
@@ -62,9 +62,9 @@ describe Hydra::AccessControls::Permission do
|
|
62
62
|
|
63
63
|
context 'with a User instance passed as :name argument' do
|
64
64
|
let(:permission) { described_class.new(type: 'person', name: user, access: 'read') }
|
65
|
-
let(:user) { FactoryGirl.
|
65
|
+
let(:user) { FactoryGirl.build(:archivist, email: 'archivist1@example.com') }
|
66
66
|
|
67
|
-
it "
|
67
|
+
it "uses string and escape agent when building" do
|
68
68
|
expect(permission.agent.first.rdf_subject.to_s).to eq 'http://projecthydra.org/ns/auth/person#archivist1@example.com'
|
69
69
|
end
|
70
70
|
end
|
@@ -134,6 +134,7 @@ describe Hydra::AccessControls::Permissions do
|
|
134
134
|
it "removes permissions on existing users" do
|
135
135
|
indexed_result = ActiveFedora::SolrService.query("id:#{subject.id}").first['edit_access_person_ssim']
|
136
136
|
expect(indexed_result).to eq ['jcoyne']
|
137
|
+
expect(subject.permissions.map(&:to_hash)).to eq [{ name: "jcoyne", type: "person", access: "edit" }]
|
137
138
|
expect(reloaded).to eq [{ name: "jcoyne", type: "person", access: "edit" }]
|
138
139
|
end
|
139
140
|
end
|
@@ -188,7 +189,7 @@ describe Hydra::AccessControls::Permissions do
|
|
188
189
|
subject.update permissions_attributes: [
|
189
190
|
{ id: permissions_id, type: "group", access: "read", name: "group1", _destroy: '1' },
|
190
191
|
{ type: "group", access: "edit", name: "group2" },
|
191
|
-
{ type: "person", access: "read", name: "joebob" }
|
192
|
+
{ type: "person", access: "read", name: "joebob" }
|
192
193
|
]
|
193
194
|
end
|
194
195
|
|
@@ -53,7 +53,7 @@ describe Hydra::PolicyAwareAbility do
|
|
53
53
|
let(:asset2) { ModsAsset.create { |a| a.admin_policy = policy2 } }
|
54
54
|
let(:asset3) { ModsAsset.create }
|
55
55
|
|
56
|
-
it "
|
56
|
+
it "retrieves the pid doc for the current object's governing policy" do
|
57
57
|
expect(instance.policy_id_for(asset.id)).to eq policy.id
|
58
58
|
expect(instance.policy_id_for(asset2.id)).to eq policy2.id
|
59
59
|
expect(instance.policy_id_for(asset3.id)).to be_nil
|
@@ -61,7 +61,7 @@ describe Hydra::PolicyAwareAbility do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
describe "policy_permissions_doc" do
|
64
|
-
it "
|
64
|
+
it "retrieves the permissions doc for the current object's policy and store for re-use" do
|
65
65
|
expect(instance).to receive(:get_permissions_solr_response_for_doc_id).with(policy.id).once.and_return("mock solr doc")
|
66
66
|
expect(instance.policy_permissions_doc(policy.id)).to eq "mock solr doc"
|
67
67
|
expect(instance.policy_permissions_doc(policy.id)).to eq "mock solr doc"
|
@@ -71,37 +71,37 @@ describe Hydra::PolicyAwareAbility do
|
|
71
71
|
|
72
72
|
describe "test_edit_from_policy" do
|
73
73
|
context "public user" do
|
74
|
-
it "
|
74
|
+
it "returns false" do
|
75
75
|
allow(instance).to receive(:user_groups).and_return(["public"])
|
76
76
|
expect(instance.test_edit_from_policy(asset.id)).to be false
|
77
77
|
end
|
78
78
|
end
|
79
79
|
context "registered user" do
|
80
|
-
it "
|
81
|
-
expect(instance.user_groups).to include("registered")
|
80
|
+
it "returns false" do
|
81
|
+
#expect(instance.user_groups).to include("registered")
|
82
82
|
expect(instance.test_edit_from_policy(asset.id)).to be false
|
83
83
|
end
|
84
84
|
end
|
85
85
|
context "user with policy read access only" do
|
86
|
-
it "
|
86
|
+
it "returns false" do
|
87
87
|
allow(instance.current_user).to receive(:user_key).and_return("nero")
|
88
88
|
expect(instance.test_edit_from_policy(asset.id)).to be false
|
89
89
|
end
|
90
90
|
end
|
91
91
|
context "user with policy edit access" do
|
92
|
-
it "
|
92
|
+
it "returns true" do
|
93
93
|
allow(instance.current_user).to receive(:user_key).and_return("julius_caesar")
|
94
94
|
expect(instance.test_edit_from_policy(asset.id)).to be true
|
95
95
|
end
|
96
96
|
end
|
97
97
|
context "user in group with policy read access" do
|
98
|
-
it "
|
98
|
+
it "returns false" do
|
99
99
|
allow(instance).to receive(:user_groups).and_return(["africana-faculty"])
|
100
100
|
expect(instance.test_edit_from_policy(asset.id)).to be false
|
101
101
|
end
|
102
102
|
end
|
103
103
|
context "user in group with policy edit access" do
|
104
|
-
it "
|
104
|
+
it "returns true" do
|
105
105
|
allow(instance).to receive(:user_groups).and_return(["cool_kids"])
|
106
106
|
expect(instance.test_edit_from_policy(asset.id)).to be true
|
107
107
|
end
|
@@ -110,37 +110,41 @@ describe Hydra::PolicyAwareAbility do
|
|
110
110
|
|
111
111
|
describe "test_read_from_policy" do
|
112
112
|
context "public user" do
|
113
|
-
it "
|
113
|
+
it "returns false" do
|
114
114
|
allow(instance).to receive(:user_groups).and_return(["public"])
|
115
115
|
expect(instance.test_read_from_policy(asset.id)).to be false
|
116
116
|
end
|
117
117
|
end
|
118
|
+
|
118
119
|
context "registered user" do
|
119
|
-
it "
|
120
|
-
expect(instance.user_groups).to include("registered")
|
120
|
+
it "returns false" do
|
121
121
|
expect(instance.test_read_from_policy(asset.id)).to be false
|
122
122
|
end
|
123
123
|
end
|
124
|
+
|
124
125
|
context "user with policy read access only" do
|
125
|
-
it "
|
126
|
+
it "returns false" do
|
126
127
|
allow(instance.current_user).to receive(:user_key).and_return("nero")
|
127
128
|
expect(instance.test_read_from_policy(asset.id)).to be true
|
128
129
|
end
|
129
130
|
end
|
131
|
+
|
130
132
|
context "user with policy edit access" do
|
131
|
-
it "
|
133
|
+
it "returns true" do
|
132
134
|
allow(instance.current_user).to receive(:user_key).and_return("julius_caesar")
|
133
135
|
expect(instance.test_read_from_policy(asset.id)).to be true
|
134
136
|
end
|
135
137
|
end
|
138
|
+
|
136
139
|
context "user in group with policy read access" do
|
137
|
-
it "
|
140
|
+
it "returns false" do
|
138
141
|
allow(instance).to receive(:user_groups).and_return(["africana-faculty"])
|
139
142
|
expect(instance.test_read_from_policy(asset.id)).to be true
|
140
143
|
end
|
141
144
|
end
|
145
|
+
|
142
146
|
context "user in group with policy edit access" do
|
143
|
-
it "
|
147
|
+
it "returns true" do
|
144
148
|
allow(instance).to receive(:user_groups).and_return(["cool_kids"])
|
145
149
|
expect(instance.test_read_from_policy(asset.id)).to be true
|
146
150
|
end
|
@@ -150,7 +154,7 @@ describe Hydra::PolicyAwareAbility do
|
|
150
154
|
describe "edit_groups_from_policy" do
|
151
155
|
subject { instance.edit_groups_from_policy(policy.id) }
|
152
156
|
|
153
|
-
it "
|
157
|
+
it "retrieves the list of groups with edit access from the policy" do
|
154
158
|
expect(subject).to match_array ["cool_kids", "in_crowd"]
|
155
159
|
end
|
156
160
|
end
|
@@ -160,7 +164,7 @@ describe Hydra::PolicyAwareAbility do
|
|
160
164
|
instance.edit_users_from_policy(policy.id)
|
161
165
|
end
|
162
166
|
|
163
|
-
it "
|
167
|
+
it "retrieves the list of individuals with edit access from the policy" do
|
164
168
|
expect(subject).to eq ["julius_caesar"]
|
165
169
|
end
|
166
170
|
end
|
@@ -168,7 +172,7 @@ describe Hydra::PolicyAwareAbility do
|
|
168
172
|
describe "read_groups_from_policy" do
|
169
173
|
subject { instance.read_groups_from_policy(policy.id) }
|
170
174
|
|
171
|
-
it "
|
175
|
+
it "retrieves the list of groups with read access from the policy" do
|
172
176
|
expect(subject).to match_array ["cool_kids", "in_crowd", "africana-faculty"]
|
173
177
|
end
|
174
178
|
end
|
@@ -176,7 +180,7 @@ describe Hydra::PolicyAwareAbility do
|
|
176
180
|
describe "read_users_from_policy" do
|
177
181
|
subject { instance.read_users_from_policy(policy.id) }
|
178
182
|
|
179
|
-
it "
|
183
|
+
it "retrieves the list of individuals with read access from the policy" do
|
180
184
|
expect(subject).to eq ["julius_caesar", "nero"]
|
181
185
|
end
|
182
186
|
end
|
@@ -101,7 +101,7 @@ describe Hydra::PolicyAwareAccessControlsEnforcement do
|
|
101
101
|
describe "policies_with_access" do
|
102
102
|
context "Authenticated user" do
|
103
103
|
before do
|
104
|
-
allow(
|
104
|
+
allow(user).to receive(:groups).and_return(["student", "africana-104-students"])
|
105
105
|
end
|
106
106
|
|
107
107
|
it "should return the policies that provide discover permissions" do
|
@@ -128,8 +128,9 @@ describe Hydra::PolicyAwareAccessControlsEnforcement do
|
|
128
128
|
describe "apply_gated_discovery" do
|
129
129
|
let(:governed_field) { ActiveFedora.index_field_mapper.solr_name('isGovernedBy', :symbol) }
|
130
130
|
let(:policy_queries) { @solr_parameters[:fq].first.split(" OR ") }
|
131
|
-
|
132
|
-
|
131
|
+
before do
|
132
|
+
allow(user).to receive(:groups).and_return(["student", "africana-104-students"])
|
133
|
+
end
|
133
134
|
|
134
135
|
context "when policies are included" do
|
135
136
|
before { subject.apply_gated_discovery(@solr_parameters) }
|
@@ -155,21 +156,28 @@ describe Hydra::PolicyAwareAccessControlsEnforcement do
|
|
155
156
|
end
|
156
157
|
|
157
158
|
describe "apply_policy_role_permissions" do
|
158
|
-
|
159
|
-
allow(
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
159
|
+
before do
|
160
|
+
allow(user).to receive(:groups).and_return(groups)
|
161
|
+
end
|
162
|
+
context "when there are slashes in the group names" do
|
163
|
+
let(:groups) { ["abc/123","cde/567"] }
|
164
|
+
it "escapes slashes" do
|
165
|
+
user_access_filters = subject.apply_policy_group_permissions
|
166
|
+
["edit","discover","read"].each do |type|
|
167
|
+
expect(user_access_filters).to include("inheritable_#{type}_access_group_ssim\:abc\\\/123")
|
168
|
+
expect(user_access_filters).to include("inheritable_#{type}_access_group_ssim\:cde\\\/567")
|
169
|
+
end
|
164
170
|
end
|
165
171
|
end
|
166
172
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
+
context "when there are spaces in the group names" do
|
174
|
+
let(:groups) { ["abc 123","cd/e 567"] }
|
175
|
+
it "escapes spaces" do
|
176
|
+
user_access_filters = subject.apply_policy_group_permissions
|
177
|
+
["edit","discover","read"].each do |type|
|
178
|
+
expect(user_access_filters).to include("inheritable_#{type}_access_group_ssim\:abc\\ 123")
|
179
|
+
expect(user_access_filters).to include("inheritable_#{type}_access_group_ssim\:cd\\\/e\\ 567")
|
180
|
+
end
|
173
181
|
end
|
174
182
|
end
|
175
183
|
end
|
@@ -1,32 +1,66 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe RoleMapper do
|
4
|
-
before do
|
5
|
-
allow(Devise).to receive(:authentication_keys).and_return(['uid'])
|
6
|
-
end
|
7
|
-
|
8
4
|
it "defines the 4 roles" do
|
9
5
|
expect(RoleMapper.role_names.sort).to eq %w(admin_policy_object_editor archivist donor patron researcher)
|
10
6
|
end
|
11
|
-
it "is quer[iy]able for roles for a given user" do
|
12
|
-
expect(RoleMapper.roles('leland_himself@example.com').sort).to eq ['archivist', 'donor', 'patron']
|
13
|
-
expect(RoleMapper.roles('archivist2@example.com')).to eq ['archivist']
|
14
|
-
end
|
15
7
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
describe "#whois" do
|
9
|
+
it "knows who is what" do
|
10
|
+
expect(RoleMapper.whois('archivist').sort).to eq %w(archivist1@example.com archivist2@example.com leland_himself@example.com)
|
11
|
+
expect(RoleMapper.whois('salesman')).to be_empty
|
12
|
+
expect(RoleMapper.whois('admin_policy_object_editor').sort).to eq %w(archivist1@example.com)
|
13
|
+
end
|
21
14
|
end
|
22
15
|
|
23
|
-
|
24
|
-
|
16
|
+
describe "fetch_groups" do
|
17
|
+
let(:user) { instance_double(User, user_key: email, new_record?: false) }
|
18
|
+
subject { RoleMapper.fetch_groups(user: user) }
|
19
|
+
|
20
|
+
context "for a user with multiple roles" do
|
21
|
+
let(:email) { 'leland_himself@example.com' }
|
22
|
+
it { is_expected.to match_array ['archivist', 'donor', 'patron'] }
|
23
|
+
|
24
|
+
it "doesn't change its response when it's called repeatedly" do
|
25
|
+
expect(subject).to match_array ['archivist', 'donor', 'patron']
|
26
|
+
expect(RoleMapper.fetch_groups(user: user)).to match_array ['archivist', 'donor', 'patron']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "for a user with a single role" do
|
31
|
+
let(:email) { 'archivist2@example.com' }
|
32
|
+
it { is_expected.to match_array ['archivist'] }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "for a user with no roles" do
|
36
|
+
let(:email) { 'zeus@olympus.mt' }
|
37
|
+
it { is_expected.to be_empty }
|
38
|
+
end
|
25
39
|
end
|
26
40
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
41
|
+
describe "roles" do
|
42
|
+
before do
|
43
|
+
allow(Deprecation).to receive(:warn)
|
44
|
+
end
|
45
|
+
it "is quer[iy]able for roles for a given user" do
|
46
|
+
expect(RoleMapper.roles('leland_himself@example.com').sort).to eq ['archivist', 'donor', 'patron']
|
47
|
+
expect(RoleMapper.roles('archivist2@example.com')).to eq ['archivist']
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when called with a user instance" do
|
51
|
+
let(:user) { User.new(email: 'leland_himself@example.com') }
|
52
|
+
before do
|
53
|
+
allow(user).to receive(:new_record?).and_return(false)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "doesn't change its response when it's called repeatedly" do
|
57
|
+
expect(RoleMapper.roles(user).sort).to eq ['archivist', 'donor', 'patron']
|
58
|
+
expect(RoleMapper.roles(user).sort).to eq ['archivist', 'donor', 'patron']
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns an empty array if there are no roles" do
|
63
|
+
expect(RoleMapper.roles('zeus@olympus.mt')).to be_empty
|
64
|
+
end
|
31
65
|
end
|
32
66
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hydra-access-controls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 10.
|
4
|
+
version: 10.4.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Beer
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2017-01-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -231,12 +231,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
231
231
|
version: 1.9.3
|
232
232
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
233
|
requirements:
|
234
|
-
- - "
|
234
|
+
- - ">"
|
235
235
|
- !ruby/object:Gem::Version
|
236
|
-
version:
|
236
|
+
version: 1.3.1
|
237
237
|
requirements: []
|
238
238
|
rubyforge_project:
|
239
|
-
rubygems_version: 2.
|
239
|
+
rubygems_version: 2.6.8
|
240
240
|
signing_key:
|
241
241
|
specification_version: 4
|
242
242
|
summary: Access controls for project hydra
|