blacklight-access_controls 0.6.2 → 0.7.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 +5 -5
- data/.rubocop.yml +13 -12
- data/.rubocop_todo.yml +9 -87
- data/.travis.yml +0 -1
- data/Rakefile +1 -2
- data/VERSION +1 -1
- data/blacklight-access_controls.gemspec +8 -8
- data/lib/blacklight/access_controls.rb +1 -0
- data/lib/blacklight/access_controls/ability.rb +2 -1
- data/lib/blacklight/access_controls/catalog.rb +5 -0
- data/lib/blacklight/access_controls/enforcement.rb +3 -2
- data/lib/blacklight/access_controls/search_builder.rb +102 -0
- data/lib/generators/blacklight/access_controls_generator.rb +5 -15
- data/solr_conf/conf/schema.xml +0 -3
- data/spec/factories/user.rb +1 -1
- data/spec/spec_helper.rb +4 -4
- data/spec/unit/ability_spec.rb +59 -53
- data/spec/unit/blacklight/access_controls/search_builder_spec.rb +113 -0
- data/spec/unit/catalog_spec.rb +1 -1
- data/spec/unit/enforcement_spec.rb +15 -15
- metadata +41 -41
- data/solr_conf/conf/schema.blacklight.xml +0 -724
- data/solr_conf/conf/schema.xml.orig +0 -1524
- data/solr_conf/conf/solrconfig.blacklight.xml +0 -411
data/solr_conf/conf/schema.xml
CHANGED
@@ -337,9 +337,6 @@
|
|
337
337
|
<!-- field for the QueryParser to use when an explicit fieldname is absent -->
|
338
338
|
<!-- <defaultSearchField>text</defaultSearchField> -->
|
339
339
|
|
340
|
-
<!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
|
341
|
-
<solrQueryParser defaultOperator="OR"/>
|
342
|
-
|
343
340
|
<!-- copyField commands copy one field to another at the time a document
|
344
341
|
is added to the index. It's used either to index the same field differently,
|
345
342
|
or to add multiple fields to the same field for easier/faster searching. -->
|
data/spec/factories/user.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -7,13 +7,13 @@ EngineCart.load_application!
|
|
7
7
|
|
8
8
|
require 'blacklight-access_controls'
|
9
9
|
|
10
|
-
require '
|
10
|
+
require 'factory_bot_rails'
|
11
11
|
require 'database_cleaner'
|
12
12
|
|
13
13
|
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
14
14
|
|
15
15
|
RSpec.configure do |config|
|
16
|
-
config.include
|
16
|
+
config.include FactoryBot::Syntax::Methods
|
17
17
|
|
18
18
|
config.include SolrSupport
|
19
19
|
|
@@ -21,12 +21,12 @@ RSpec.configure do |config|
|
|
21
21
|
DatabaseCleaner.clean_with :truncation
|
22
22
|
end
|
23
23
|
|
24
|
-
config.before
|
24
|
+
config.before do
|
25
25
|
DatabaseCleaner.strategy = :transaction
|
26
26
|
DatabaseCleaner.start
|
27
27
|
end
|
28
28
|
|
29
|
-
config.after
|
29
|
+
config.after do
|
30
30
|
DatabaseCleaner.clean
|
31
31
|
end
|
32
32
|
end
|
data/spec/unit/ability_spec.rb
CHANGED
@@ -3,38 +3,40 @@
|
|
3
3
|
require 'cancan/matchers'
|
4
4
|
|
5
5
|
describe Ability do
|
6
|
-
let(:ability) {
|
6
|
+
let(:ability) { described_class.new(user) }
|
7
7
|
|
8
8
|
describe 'class methods' do
|
9
9
|
it 'has keys for access control fields' do
|
10
|
-
expect(
|
11
|
-
expect(
|
12
|
-
expect(
|
13
|
-
expect(
|
14
|
-
expect(
|
15
|
-
expect(
|
10
|
+
expect(described_class.read_group_field).to eq 'read_access_group_ssim'
|
11
|
+
expect(described_class.read_user_field).to eq 'read_access_person_ssim'
|
12
|
+
expect(described_class.discover_group_field).to eq 'discover_access_group_ssim'
|
13
|
+
expect(described_class.discover_user_field).to eq 'discover_access_person_ssim'
|
14
|
+
expect(described_class.download_group_field).to eq 'download_access_group_ssim'
|
15
|
+
expect(described_class.download_user_field).to eq 'download_access_person_ssim'
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe 'Given an asset that has been made publicly discoverable' do
|
20
|
-
let(:asset)
|
21
|
-
|
20
|
+
let(:asset) do
|
21
|
+
SolrDocument.new(id: 'public_discovery',
|
22
|
+
discover_access_group_ssim: ['public'])
|
23
|
+
end
|
22
24
|
|
23
25
|
context 'Then a not-signed-in user' do
|
24
|
-
let(:user) { nil }
|
25
|
-
|
26
26
|
subject { ability }
|
27
27
|
|
28
|
+
let(:user) { nil }
|
29
|
+
|
28
30
|
it { is_expected.to be_able_to(:discover, asset) }
|
29
31
|
it { is_expected.not_to be_able_to(:read, asset) }
|
30
32
|
it { is_expected.not_to be_able_to(:download, asset) }
|
31
33
|
end
|
32
34
|
|
33
35
|
context 'Then a registered user' do
|
34
|
-
let(:user) { create(:user) }
|
35
|
-
|
36
36
|
subject { ability }
|
37
37
|
|
38
|
+
let(:user) { create(:user) }
|
39
|
+
|
38
40
|
it { is_expected.to be_able_to(:discover, asset) }
|
39
41
|
it { is_expected.not_to be_able_to(:read, asset) }
|
40
42
|
it { is_expected.not_to be_able_to(:download, asset) }
|
@@ -44,10 +46,10 @@ describe Ability do
|
|
44
46
|
subject { ability }
|
45
47
|
|
46
48
|
let(:user) { create(:user) }
|
47
|
-
let(:asset)
|
49
|
+
let(:asset) do
|
48
50
|
create_solr_doc(id: 'public_discovery',
|
49
51
|
discover_access_group_ssim: ['public'])
|
50
|
-
|
52
|
+
end
|
51
53
|
|
52
54
|
# It should still work, even if we just pass in an ID
|
53
55
|
it { is_expected.to be_able_to(:discover, asset.id) }
|
@@ -57,24 +59,26 @@ describe Ability do
|
|
57
59
|
end
|
58
60
|
|
59
61
|
describe 'Given an asset that has been made publicly readable' do
|
60
|
-
let(:asset)
|
61
|
-
|
62
|
+
let(:asset) do
|
63
|
+
SolrDocument.new(id: 'public_read',
|
64
|
+
read_access_group_ssim: ['public'])
|
65
|
+
end
|
62
66
|
|
63
67
|
context 'Then a not-signed-in user' do
|
64
|
-
let(:user) { nil }
|
65
|
-
|
66
68
|
subject { ability }
|
67
69
|
|
70
|
+
let(:user) { nil }
|
71
|
+
|
68
72
|
it { is_expected.to be_able_to(:discover, asset) }
|
69
73
|
it { is_expected.to be_able_to(:read, asset) }
|
70
74
|
it { is_expected.not_to be_able_to(:download, asset) }
|
71
75
|
end
|
72
76
|
|
73
77
|
context 'Then a registered user' do
|
74
|
-
let(:user) { create(:user) }
|
75
|
-
|
76
78
|
subject { ability }
|
77
79
|
|
80
|
+
let(:user) { create(:user) }
|
81
|
+
|
78
82
|
it { is_expected.to be_able_to(:discover, asset) }
|
79
83
|
it { is_expected.to be_able_to(:read, asset) }
|
80
84
|
it { is_expected.not_to be_able_to(:download, asset) }
|
@@ -84,10 +88,10 @@ describe Ability do
|
|
84
88
|
subject { ability }
|
85
89
|
|
86
90
|
let(:user) { create(:user) }
|
87
|
-
let(:asset)
|
91
|
+
let(:asset) do
|
88
92
|
create_solr_doc(id: 'public_read',
|
89
93
|
read_access_group_ssim: ['public'])
|
90
|
-
|
94
|
+
end
|
91
95
|
|
92
96
|
# It should still work, even if we just pass in an ID
|
93
97
|
it { is_expected.to be_able_to(:discover, asset.id) }
|
@@ -98,14 +102,16 @@ describe Ability do
|
|
98
102
|
|
99
103
|
describe 'Given an asset that has been made publicly downloadable' do
|
100
104
|
let(:id) { 'public_download' }
|
101
|
-
let(:asset)
|
102
|
-
|
105
|
+
let(:asset) do
|
106
|
+
SolrDocument.new(id: id,
|
107
|
+
download_access_group_ssim: ['public'])
|
108
|
+
end
|
103
109
|
|
104
110
|
context 'Then a not-signed-in user' do
|
105
|
-
let(:user) { nil }
|
106
|
-
|
107
111
|
subject { ability }
|
108
112
|
|
113
|
+
let(:user) { nil }
|
114
|
+
|
109
115
|
it { is_expected.to be_able_to(:discover, asset) }
|
110
116
|
it { is_expected.to be_able_to(:read, asset) }
|
111
117
|
it { is_expected.to be_able_to(:download, asset) }
|
@@ -125,10 +131,10 @@ describe Ability do
|
|
125
131
|
subject { ability }
|
126
132
|
|
127
133
|
let(:user) { create(:user) }
|
128
|
-
let(:asset)
|
134
|
+
let(:asset) do
|
129
135
|
create_solr_doc(id: id,
|
130
136
|
download_access_group_ssim: ['public'])
|
131
|
-
|
137
|
+
end
|
132
138
|
|
133
139
|
# It should still work, even if we just pass in an ID
|
134
140
|
it { is_expected.to be_able_to(:discover, asset.id) }
|
@@ -142,30 +148,30 @@ describe Ability do
|
|
142
148
|
let(:asset) { SolrDocument.new(id: 'user_disco', discover_access_person_ssim: [user_with_access.email]) }
|
143
149
|
|
144
150
|
context 'Then a not-signed-in user' do
|
145
|
-
let(:user) { nil }
|
146
|
-
|
147
151
|
subject { ability }
|
148
152
|
|
153
|
+
let(:user) { nil }
|
154
|
+
|
149
155
|
it { is_expected.not_to be_able_to(:discover, asset) }
|
150
156
|
it { is_expected.not_to be_able_to(:read, asset) }
|
151
157
|
it { is_expected.not_to be_able_to(:download, asset) }
|
152
158
|
end
|
153
159
|
|
154
160
|
context 'Then a different registered user' do
|
155
|
-
let(:user) { create(:user) }
|
156
|
-
|
157
161
|
subject { ability }
|
158
162
|
|
163
|
+
let(:user) { create(:user) }
|
164
|
+
|
159
165
|
it { is_expected.not_to be_able_to(:discover, asset) }
|
160
166
|
it { is_expected.not_to be_able_to(:read, asset) }
|
161
167
|
it { is_expected.not_to be_able_to(:download, asset) }
|
162
168
|
end
|
163
169
|
|
164
170
|
context 'Then that user' do
|
165
|
-
let(:user) { user_with_access }
|
166
|
-
|
167
171
|
subject { ability }
|
168
172
|
|
173
|
+
let(:user) { user_with_access }
|
174
|
+
|
169
175
|
it { is_expected.to be_able_to(:discover, asset) }
|
170
176
|
it { is_expected.not_to be_able_to(:read, asset) }
|
171
177
|
it { is_expected.not_to be_able_to(:download, asset) }
|
@@ -177,30 +183,30 @@ describe Ability do
|
|
177
183
|
let(:asset) { SolrDocument.new(id: 'user_read', read_access_person_ssim: [user_with_access.email]) }
|
178
184
|
|
179
185
|
context 'Then a not-signed-in user' do
|
180
|
-
let(:user) { nil }
|
181
|
-
|
182
186
|
subject { ability }
|
183
187
|
|
188
|
+
let(:user) { nil }
|
189
|
+
|
184
190
|
it { is_expected.not_to be_able_to(:discover, asset) }
|
185
191
|
it { is_expected.not_to be_able_to(:read, asset) }
|
186
192
|
it { is_expected.not_to be_able_to(:download, asset) }
|
187
193
|
end
|
188
194
|
|
189
195
|
context 'Then a different registered user' do
|
190
|
-
let(:user) { create(:user) }
|
191
|
-
|
192
196
|
subject { ability }
|
193
197
|
|
198
|
+
let(:user) { create(:user) }
|
199
|
+
|
194
200
|
it { is_expected.not_to be_able_to(:discover, asset) }
|
195
201
|
it { is_expected.not_to be_able_to(:read, asset) }
|
196
202
|
it { is_expected.not_to be_able_to(:download, asset) }
|
197
203
|
end
|
198
204
|
|
199
205
|
context 'Then that user' do
|
200
|
-
let(:user) { user_with_access }
|
201
|
-
|
202
206
|
subject { ability }
|
203
207
|
|
208
|
+
let(:user) { user_with_access }
|
209
|
+
|
204
210
|
it { is_expected.to be_able_to(:discover, asset) }
|
205
211
|
it { is_expected.to be_able_to(:read, asset) }
|
206
212
|
it { is_expected.not_to be_able_to(:download, asset) }
|
@@ -212,30 +218,30 @@ describe Ability do
|
|
212
218
|
let(:asset) { SolrDocument.new(id: 'user_read', download_access_person_ssim: [user_with_access.email]) }
|
213
219
|
|
214
220
|
context 'Then a not-signed-in user' do
|
215
|
-
let(:user) { nil }
|
216
|
-
|
217
221
|
subject { ability }
|
218
222
|
|
223
|
+
let(:user) { nil }
|
224
|
+
|
219
225
|
it { is_expected.not_to be_able_to(:discover, asset) }
|
220
226
|
it { is_expected.not_to be_able_to(:read, asset) }
|
221
227
|
it { is_expected.not_to be_able_to(:download, asset) }
|
222
228
|
end
|
223
229
|
|
224
230
|
context 'Then a different registered user' do
|
225
|
-
let(:user) { create(:user) }
|
226
|
-
|
227
231
|
subject { ability }
|
228
232
|
|
233
|
+
let(:user) { create(:user) }
|
234
|
+
|
229
235
|
it { is_expected.not_to be_able_to(:discover, asset) }
|
230
236
|
it { is_expected.not_to be_able_to(:read, asset) }
|
231
237
|
it { is_expected.not_to be_able_to(:download, asset) }
|
232
238
|
end
|
233
239
|
|
234
240
|
context 'Then that user' do
|
235
|
-
let(:user) { user_with_access }
|
236
|
-
|
237
241
|
subject { ability }
|
238
242
|
|
243
|
+
let(:user) { user_with_access }
|
244
|
+
|
239
245
|
it { is_expected.to be_able_to(:discover, asset) }
|
240
246
|
it { is_expected.to be_able_to(:read, asset) }
|
241
247
|
it { is_expected.to be_able_to(:download, asset) }
|
@@ -249,13 +255,13 @@ describe Ability do
|
|
249
255
|
end
|
250
256
|
|
251
257
|
describe '#guest_user' do
|
252
|
-
let(:user) { nil }
|
253
|
-
|
254
258
|
subject { ability.guest_user }
|
255
259
|
|
260
|
+
let(:user) { nil }
|
261
|
+
|
256
262
|
it 'is a new user' do
|
257
263
|
expect(subject).to be_a User
|
258
|
-
expect(subject
|
264
|
+
expect(subject).to be_new_record
|
259
265
|
end
|
260
266
|
end
|
261
267
|
|
@@ -275,17 +281,17 @@ describe Ability do
|
|
275
281
|
end
|
276
282
|
|
277
283
|
context 'a user with groups' do
|
278
|
-
let(:user) { double(groups: %w
|
284
|
+
let(:user) { double(groups: %w[group1 group2], new_record?: false) }
|
279
285
|
|
280
286
|
it { is_expected.to include('group1', 'group2') }
|
281
287
|
end
|
282
288
|
end
|
283
289
|
|
284
290
|
describe 'with a custom method' do
|
285
|
-
let(:user) { create(:user) }
|
286
|
-
|
287
291
|
subject { MyAbility.new(user) }
|
288
292
|
|
293
|
+
let(:user) { create(:user) }
|
294
|
+
|
289
295
|
before do
|
290
296
|
class MyAbility
|
291
297
|
include Blacklight::AccessControls::Ability
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Blacklight::AccessControls::SearchBuilder do
|
4
|
+
subject { search_builder }
|
5
|
+
|
6
|
+
let(:search_builder) do
|
7
|
+
described_class.new(controller, ability: ability)
|
8
|
+
end
|
9
|
+
let(:controller) { double }
|
10
|
+
let(:user) { User.new }
|
11
|
+
let(:ability) { Ability.new(user) }
|
12
|
+
|
13
|
+
describe '#discovery_permissions' do
|
14
|
+
subject { search_builder.default_permission_types }
|
15
|
+
|
16
|
+
it { is_expected.to eq %w[discover read] }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#apply_gated_discovery' do
|
20
|
+
let(:fq_first) do
|
21
|
+
solr_parameters = {}
|
22
|
+
search_builder.send(:apply_gated_discovery, solr_parameters)
|
23
|
+
solr_parameters[:fq].first
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'logger' do
|
27
|
+
# Expectation will be triggered by Ability class (that calls Rails.logger.debug earlier). So we double Ability to avoid false positive.
|
28
|
+
let(:ability) { instance_double(Ability, user_groups: [], current_user: user) }
|
29
|
+
|
30
|
+
it 'is called with debug' do
|
31
|
+
expect(Rails.logger).to receive(:debug).with(/^Solr parameters/)
|
32
|
+
search_builder.send(:apply_gated_discovery, {})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'Given I am not logged in' do
|
37
|
+
it "Then I should be treated as a member of the 'public' group" do
|
38
|
+
expect(fq_first).to eq '({!terms f=discover_access_group_ssim}public) OR ({!terms f=read_access_group_ssim}public)'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Then I should not be treated as a member of the 'registered' group" do
|
42
|
+
expect(fq_first).not_to match(/registered/)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'Given I am a registered user' do
|
47
|
+
let(:groups) { %w[faculty africana-faculty] }
|
48
|
+
let(:user) do
|
49
|
+
create(:user).tap do |u|
|
50
|
+
allow(u).to receive(:groups) { groups }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'searches for my user key in discover and read fields' do
|
55
|
+
expect(fq_first).to match(/discover_access_person_ssim\:#{user.user_key}/)
|
56
|
+
expect(fq_first).to match(/read_access_person_ssim\:#{user.user_key}/)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'searches for my groups' do
|
60
|
+
expect(fq_first).to match(/\{!terms f=discover_access_group_ssim\}public,faculty,africana-faculty,registered/)
|
61
|
+
expect(fq_first).to match(/\{!terms f=read_access_group_ssim\}public,faculty,africana-faculty,registered/)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'does not build empty clauses' do
|
65
|
+
expect(search_builder).to receive(:apply_user_permissions).and_return(['({!terms f=discover_access_group_ssim}public,faculty,africana-faculty,registered)', '', nil])
|
66
|
+
expect(fq_first).not_to match(/ OR $/) # i.e. doesn't end w/ empty
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'slashes in the group names' do
|
70
|
+
let(:groups) { ['abc/123', 'cde/567'] }
|
71
|
+
|
72
|
+
it 'does not escape slashes' do
|
73
|
+
expect(fq_first).to match(%r{\{!terms f=discover_access_group_ssim\}public,abc/123,cde/567,registered})
|
74
|
+
expect(fq_first).to match(%r{\{!terms f=read_access_group_ssim\}public,abc/123,cde/567,registered})
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'spaces in the group names' do
|
79
|
+
let(:groups) { ['abc 123', 'cd/e 567'] }
|
80
|
+
|
81
|
+
it 'does not escape spaces in group names' do
|
82
|
+
expect(fq_first).to match(%r{\{!terms f=discover_access_group_ssim\}public,abc 123,cd/e 567,registered})
|
83
|
+
expect(fq_first).to match(%r{\{!terms f=read_access_group_ssim\}public,abc 123,cd/e 567,registered})
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'colons in the groups names' do
|
88
|
+
let(:groups) { ['abc:123', 'cde:567'] }
|
89
|
+
|
90
|
+
it 'does not escape colons' do
|
91
|
+
expect(fq_first).to match(/\{!terms f=discover_access_group_ssim\}public,abc:123,cde:567,registered/)
|
92
|
+
expect(fq_first).to match(/\{!terms f=read_access_group_ssim\}public,abc:123,cde:567,registered/)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#apply_user_permissions' do
|
99
|
+
describe 'when the user is a guest user (user key nil)' do
|
100
|
+
it 'does not create filters' do
|
101
|
+
expect(search_builder.send(:apply_user_permissions)).to eq []
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'when the user is a guest user (user key empty string)' do
|
106
|
+
let(:user) { User.new(email: '') }
|
107
|
+
|
108
|
+
it 'does not create filters' do
|
109
|
+
expect(search_builder.send(:apply_user_permissions)).to eq []
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|