blacklight-access_controls 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 715bccb2faf2cc502417ad8d1f9052baf98c2c2e
4
- data.tar.gz: c011219a6d76e3c0e6bba5640c9f5c92eb5fe300
3
+ metadata.gz: e429877da325ab5e409d1d62676ef8d5e1fc0495
4
+ data.tar.gz: c77b7f0cf4e24cdaa8acfe763e92aacd16b36f64
5
5
  SHA512:
6
- metadata.gz: d80acc63c003ee2d3b84b5790bd5a755cea409efb58ee4311fd6e5b236712c3fbbb92e9609f6e93e948bc7cfae7bd0938dfad4f51e6d109a603ce3dc4ea4dfdd
7
- data.tar.gz: 54dfe0310b6df052616602eb53d0f916e431717f0a918b0d436e992d8ee9eacb03312d5b69de7be2d880f62bfc0ee1d10db2b653c66b60195321790a526cafad
6
+ metadata.gz: 054afd2481955b5278a523a11d57723b0d1c8b6fe27d18e5956abae262b2d93ed77e9775b0ad582f70d73269d4e9c69a3c790a5aa5b0e7884e59db2a918c9523
7
+ data.tar.gz: d679058bc3c0aa1a782f992433678e229f10d892e81257dfe9329601dcfefaeb0db3af6e579706781a9393ae30f48725c7b9b6f5b1e21035269182b974a200f2
data/README.textile CHANGED
@@ -21,20 +21,65 @@ h3. Configure solr
21
21
  h3. Run the generator
22
22
 
23
23
  <pre>
24
- rails generate blacklight:access_controls
24
+ rails generate blacklight:access_controls
25
25
  </pre>
26
26
 
27
+ If you want to use a different user model than "User", you can pass an argument to the generator
28
+
29
+ <pre>
30
+ rails generate blacklight:access_controls -m Student
31
+ </pre>
32
+
33
+ If you have more than one search builder class, or if your search builder is located someplace other than app/models/search_builder.rb, you can pass an argument to the generator
34
+
35
+ <pre>
36
+ rails generate blacklight:access_controls -b app/search_builders/search_one.rb app/search_builders/search_two.rb
37
+ </pre>
38
+
39
+ h3. Implement user.groups
40
+
41
+ * If you are using LDAP or some other way of controlling group-level access to records, implement a 'groups' method on your User model. If you call user.groups method on a user, it should return a list of groups that the user belongs to.
42
+
27
43
 
28
44
  h2. Using Access Controls
29
45
 
30
46
  Some notes about using blacklight-access_controls within your Blacklight app:
31
47
 
32
- * You can grant access to a record to specific users or groups by adding them to the correct fields in the solr document. For example, discover_access_group_ssim: "public" or read_access_person_ssim: "frodo@example.com".
48
+ * Blacklight Access Controls allows 3 types of access to a record: discover, read, or download. Of course, you can change or redefine the meaning of these access levels in your own app, but within the context of blacklight-access_controls, the 3 levels of access mean this:
49
+ ** Discover access allows the user to see that a record does exist and view minimal metadata about the record, but the user may not be allowed to read or download the full record. (e.g. The user can see the record in a search results list, but if they visit the show page of the record, they will be denied access.)
50
+ ** Read access allows the user to view the entire record, but the user might not be able to download attached files.
51
+ ** Download access allows the user to download the full record and/or files that are attached to the record.
52
+
53
+ * The access levels are hierarchical, so if you grant "download" access to a user, the user will automatically be granted "read" and "discover" access. If you grant "read" access, the user will also have "discover" access, but not "download" access.
54
+
55
+ * You can grant access to a record to specific users or groups by adding them to the correct fields in the solr document. For example:
56
+
57
+ <pre>
58
+ {
59
+ discover_access_group_ssim: "public",
60
+ read_access_person_ssim: "bilbo@example.com",
61
+ download_access_person_ssim: "frodo@example.com"
62
+ }
63
+ </pre>
33
64
 
34
65
  * The gem expects user.groups to return a list of groups that the user belongs to. By default, all users belong to a group called "public", and all logged-in users belong to a group called "registered".
35
66
 
36
67
  * If you want a record to be readable by the public, you need to add "public" to the "read_access_group_ssim" field in the solr document, or if you want discover-only access, add "public" to "discover_access_group_ssim". (Discover-only means that the user can see that the record exists in a catalog search, but won't be able to view the record itself.)
37
68
 
69
+ * If you grant download access to a user or group, blacklight-access_controls will grant access only to the SolrDocument. If you want the user to be able to download a different type of object such as an attached file, you may need to add that permission into your cancan abilities. In this example, we check the permissions of the user id for the object itself, instead of for the SolrDocument that indexes the object:
70
+
71
+ <pre>
72
+ can :download, AttachedFile, parent: { user_id: user.id }
73
+ </pre>
74
+
75
+ * If you want to test download access against the solr document instead of against the object (for example, to decide whether or not to display a download link on an index or show page), then you can just add a normal cancan check in your controller or view. There is no need to add anything special to the cancan ability class.
76
+
77
+ <pre>
78
+ if can? :download, solr_document
79
+ # do something
80
+ end
81
+ </pre>
82
+
38
83
 
39
84
  h2. Developer Notes
40
85
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -6,7 +6,6 @@ module Blacklight
6
6
  autoload :User
7
7
  autoload :PermissionsQuery
8
8
  autoload :PermissionsCache
9
- autoload :PermissionsSolrDocument
10
9
  autoload :Ability
11
10
  autoload :Enforcement
12
11
  autoload :Catalog
@@ -11,9 +11,9 @@ module Blacklight
11
11
 
12
12
  # Once you include this module, you can add custom
13
13
  # permission methods to ability_logic, like so:
14
- # self.ability_logic +=[:setup_my_permissions]
14
+ # self.ability_logic += [:setup_my_permissions]
15
15
  class_attribute :ability_logic
16
- self.ability_logic = [:discover_permissions, :read_permissions]
16
+ self.ability_logic = [:discover_permissions, :read_permissions, :download_permissions]
17
17
  end
18
18
 
19
19
  def initialize(user, options={})
@@ -63,6 +63,17 @@ module Blacklight
63
63
  end
64
64
  end
65
65
 
66
+ def download_permissions
67
+ can :download, String do |id|
68
+ test_download(id)
69
+ end
70
+
71
+ can :download, SolrDocument do |obj|
72
+ cache.put(obj.id, obj)
73
+ test_download(obj.id)
74
+ end
75
+ end
76
+
66
77
  def test_discover(id)
67
78
  Rails.logger.debug("[CANCAN] Checking discover permissions for user: #{current_user.user_key} with groups: #{user_groups.inspect}")
68
79
  group_intersection = user_groups & discover_groups(id)
@@ -75,6 +86,12 @@ module Blacklight
75
86
  !group_intersection.empty? || read_users(id).include?(current_user.user_key)
76
87
  end
77
88
 
89
+ def test_download(id)
90
+ Rails.logger.debug("[CANCAN] Checking download permissions for user: #{current_user.user_key} with groups: #{user_groups.inspect}")
91
+ group_intersection = user_groups & download_groups(id)
92
+ !group_intersection.empty? || download_users(id).include?(current_user.user_key)
93
+ end
94
+
78
95
  # You can override this method if you are using a different AuthZ (such as LDAP)
79
96
  def user_groups
80
97
  return @user_groups if @user_groups
@@ -108,22 +125,40 @@ module Blacklight
108
125
  dp
109
126
  end
110
127
 
128
+ # download access implies read access, so read_groups is the union of download and read groups.
111
129
  def read_groups(id)
112
130
  doc = permissions_doc(id)
113
131
  return [] if doc.nil?
114
- rg = Array(doc[self.class.read_group_field])
132
+ rg = download_groups(id) | Array(doc[self.class.read_group_field])
115
133
  Rails.logger.debug("[CANCAN] read_groups: #{rg.inspect}")
116
134
  rg
117
135
  end
118
136
 
137
+ # download access implies read access, so read_users is the union of download and read users.
119
138
  def read_users(id)
120
139
  doc = permissions_doc(id)
121
140
  return [] if doc.nil?
122
- rp = Array(doc[self.class.read_user_field])
141
+ rp = download_users(id) | Array(doc[self.class.read_user_field])
123
142
  Rails.logger.debug("[CANCAN] read_users: #{rp.inspect}")
124
143
  rp
125
144
  end
126
145
 
146
+ def download_groups(id)
147
+ doc = permissions_doc(id)
148
+ return [] if doc.nil?
149
+ dg = Array(doc[self.class.download_group_field])
150
+ Rails.logger.debug("[CANCAN] download_groups: #{dg.inspect}")
151
+ dg
152
+ end
153
+
154
+ def download_users(id)
155
+ doc = permissions_doc(id)
156
+ return [] if doc.nil?
157
+ dp = Array(doc[self.class.download_user_field])
158
+ Rails.logger.debug("[CANCAN] download_users: #{dp.inspect}")
159
+ dp
160
+ end
161
+
127
162
  module ClassMethods
128
163
 
129
164
  def discover_group_field
@@ -142,6 +177,14 @@ module Blacklight
142
177
  Blacklight::AccessControls.config.read_user_field
143
178
  end
144
179
 
180
+ def download_group_field
181
+ Blacklight::AccessControls.config.download_group_field
182
+ end
183
+
184
+ def download_user_field
185
+ Blacklight::AccessControls.config.download_user_field
186
+ end
187
+
145
188
  end
146
189
  end
147
190
  end
@@ -4,15 +4,21 @@ module Blacklight
4
4
 
5
5
  def initialize
6
6
  @user_model = default_user_model
7
+
7
8
  @discover_group_field = default_discover_group_field
8
9
  @discover_user_field = default_discover_user_field
10
+
9
11
  @read_group_field = default_read_group_field
10
12
  @read_user_field = default_read_user_field
13
+
14
+ @download_group_field = default_download_group_field
15
+ @download_user_field = default_download_user_field
11
16
  end
12
17
 
13
18
  attr_accessor :user_model
14
19
  attr_accessor :discover_group_field, :discover_user_field
15
20
  attr_accessor :read_group_field, :read_user_field
21
+ attr_accessor :download_group_field, :download_user_field
16
22
 
17
23
  def default_user_model
18
24
  'User'
@@ -34,6 +40,14 @@ module Blacklight
34
40
  "read_access_person_ssim"
35
41
  end
36
42
 
43
+ def default_download_group_field
44
+ "download_access_group_ssim"
45
+ end
46
+
47
+ def default_download_user_field
48
+ "download_access_person_ssim"
49
+ end
50
+
37
51
  end
38
52
  end
39
53
  end
@@ -12,7 +12,7 @@ module Blacklight::AccessControls
12
12
  end
13
13
 
14
14
  def permissions_document_class
15
- Blacklight::AccessControls::PermissionsSolrDocument
15
+ SolrDocument
16
16
  end
17
17
 
18
18
  protected
@@ -11,18 +11,29 @@ module Blacklight
11
11
 
12
12
  source_root File.expand_path("..", __FILE__)
13
13
 
14
+ class_option :user_model, aliases: '-m',
15
+ type: :string, default: 'User',
16
+ desc: "What is your user model called?"
17
+
18
+ class_option :search_builders, aliases: '-b', type: :array,
19
+ default: Array(File.join('app', 'models', 'search_builder.rb')),
20
+ desc: "The path(s) to your search builder model(s)"
21
+
22
+
14
23
  def add_access_controls_to_user
15
- say_status('status', 'ADD ACCESS CONTROLS TO USER', :yellow)
16
- insert_into_file 'app/models/user.rb',
24
+ say_status('status', 'ADDING ACCESS CONTROLS TO USER MODEL', :yellow)
25
+ insert_into_file File.join('app','models', "#{options[:user_model].underscore}.rb"),
17
26
  " include Blacklight::AccessControls::User\n\n",
18
27
  after: "include Blacklight::User\n"
19
28
  end
20
29
 
21
30
  def add_access_controls_to_search_builder
22
- say_status('status', 'ADDING ACCESS CONTROLS TO SEARCH BUILDER', :yellow)
23
- insert_into_file 'app/models/search_builder.rb',
24
- " include Blacklight::AccessControls::Enforcement\n",
25
- before: "end"
31
+ say_status('status', 'ADDING ACCESS CONTROLS TO SEARCH BUILDERS', :yellow)
32
+ options[:search_builders].each do |file_path|
33
+ insert_into_file file_path,
34
+ " include Blacklight::AccessControls::Enforcement\n\n",
35
+ after: "include Blacklight::Solr::SearchBuilderBehavior\n"
36
+ end
26
37
  end
27
38
 
28
39
  def add_access_controls_to_catalog_controller
@@ -317,14 +317,12 @@
317
317
  access_ssim,
318
318
  discover_access_group_ssim,discover_access_person_ssim,
319
319
  read_access_group_ssim,read_access_person_ssim,
320
- edit_access_group_ssim,edit_access_person_ssim,
320
+ download_access_group_ssim,download_access_person_ssim,
321
321
  depositor_ti,
322
- embargo_release_date_dtsi
323
322
  inheritable_access_ssim,
324
323
  inheritable_discover_access_group_ssim,inheritable_discover_access_person_ssim,
325
324
  inheritable_read_access_group_ssim,inheritable_read_access_person_ssim,
326
- inheritable_edit_access_group_ssim,inheritable_edit_access_person_ssim,
327
- inheritable_embargo_release_date_dtsi
325
+ inheritable_download_access_group_ssim,inheritable_download_access_person_ssim,
328
326
  </str>
329
327
  </lst>
330
328
  </requestHandler>
@@ -10,6 +10,8 @@ describe Ability do
10
10
  expect(Ability.read_user_field).to eq 'read_access_person_ssim'
11
11
  expect(Ability.discover_group_field).to eq 'discover_access_group_ssim'
12
12
  expect(Ability.discover_user_field).to eq 'discover_access_person_ssim'
13
+ expect(Ability.download_group_field).to eq 'download_access_group_ssim'
14
+ expect(Ability.download_user_field).to eq 'download_access_person_ssim'
13
15
  end
14
16
  end
15
17
 
@@ -23,6 +25,7 @@ describe Ability do
23
25
 
24
26
  it { should be_able_to(:discover, asset) }
25
27
  it { should_not be_able_to(:read, asset) }
28
+ it { should_not be_able_to(:download, asset) }
26
29
  end
27
30
 
28
31
  context "Then a registered user" do
@@ -31,6 +34,7 @@ describe Ability do
31
34
 
32
35
  it { should be_able_to(:discover, asset) }
33
36
  it { should_not be_able_to(:read, asset) }
37
+ it { should_not be_able_to(:download, asset) }
34
38
  end
35
39
 
36
40
  context 'With an ID instead of a SolrDocument' do
@@ -45,6 +49,7 @@ describe Ability do
45
49
  # It should still work, even if we just pass in an ID
46
50
  it { should be_able_to(:discover, asset.id) }
47
51
  it { should_not be_able_to(:read, asset.id) }
52
+ it { should_not be_able_to(:download, asset.id) }
48
53
  end
49
54
  end
50
55
 
@@ -52,12 +57,52 @@ describe Ability do
52
57
  let(:asset) { SolrDocument.new(id: 'public_read',
53
58
  read_access_group_ssim: ['public']) }
54
59
 
60
+ context "Then a not-signed-in user" do
61
+ let(:user) { nil }
62
+ subject { ability }
63
+
64
+ it { should be_able_to(:discover, asset) }
65
+ it { should be_able_to(:read, asset) }
66
+ it { should_not be_able_to(:download, asset) }
67
+ end
68
+
69
+ context "Then a registered user" do
70
+ let(:user) { create(:user) }
71
+ subject { ability }
72
+
73
+ it { should be_able_to(:discover, asset) }
74
+ it { should be_able_to(:read, asset) }
75
+ it { should_not be_able_to(:download, asset) }
76
+ end
77
+
78
+ context 'With an ID instead of a SolrDocument' do
79
+ let(:user) { create(:user) }
80
+ subject { ability }
81
+
82
+ let(:asset) {
83
+ create_solr_doc(id: 'public_read',
84
+ read_access_group_ssim: ['public'])
85
+ }
86
+
87
+ # It should still work, even if we just pass in an ID
88
+ it { should be_able_to(:discover, asset.id) }
89
+ it { should be_able_to(:read, asset.id) }
90
+ it { should_not be_able_to(:download, asset.id) }
91
+ end
92
+ end
93
+
94
+ describe "Given an asset that has been made publicly downloadable" do
95
+ let(:id) { 'public_download' }
96
+ let(:asset) { SolrDocument.new(id: id,
97
+ download_access_group_ssim: ['public']) }
98
+
55
99
  context "Then a not-signed-in user" do
56
100
  let(:user) { nil }
57
101
  subject { ability }
58
102
 
59
103
  it { should be_able_to(:discover, asset) }
60
104
  it { should be_able_to(:read, asset) }
105
+ it { should be_able_to(:download, asset) }
61
106
  end
62
107
 
63
108
  context "Then a registered user" do
@@ -66,23 +111,26 @@ describe Ability do
66
111
 
67
112
  it { should be_able_to(:discover, asset) }
68
113
  it { should be_able_to(:read, asset) }
114
+ it { should be_able_to(:download, asset) }
69
115
  end
70
116
 
71
- context 'With an ID instead of a SolrDocument' do
117
+ context 'With an ID instead of a record' do
72
118
  let(:user) { create(:user) }
73
119
  subject { ability }
74
120
 
75
121
  let(:asset) {
76
- create_solr_doc(id: 'public_read',
77
- read_access_group_ssim: ['public'])
122
+ create_solr_doc(id: id,
123
+ download_access_group_ssim: ['public'])
78
124
  }
79
125
 
80
126
  # It should still work, even if we just pass in an ID
81
127
  it { should be_able_to(:discover, asset.id) }
82
128
  it { should be_able_to(:read, asset.id) }
129
+ it { should be_able_to(:download, asset.id) }
83
130
  end
84
131
  end
85
132
 
133
+
86
134
  describe "Given an asset to which a specific user has discovery access" do
87
135
  let(:user_with_access) { create(:user) }
88
136
  let(:asset) { SolrDocument.new(id: 'user_disco', discover_access_person_ssim: [user_with_access.email]) }
@@ -93,6 +141,7 @@ describe Ability do
93
141
 
94
142
  it { should_not be_able_to(:discover, asset) }
95
143
  it { should_not be_able_to(:read, asset) }
144
+ it { should_not be_able_to(:download, asset) }
96
145
  end
97
146
 
98
147
  context "Then a different registered user" do
@@ -101,6 +150,7 @@ describe Ability do
101
150
 
102
151
  it { should_not be_able_to(:discover, asset) }
103
152
  it { should_not be_able_to(:read, asset) }
153
+ it { should_not be_able_to(:download, asset) }
104
154
  end
105
155
 
106
156
  context "Then that user" do
@@ -109,6 +159,7 @@ describe Ability do
109
159
 
110
160
  it { should be_able_to(:discover, asset) }
111
161
  it { should_not be_able_to(:read, asset) }
162
+ it { should_not be_able_to(:download, asset) }
112
163
  end
113
164
  end
114
165
 
@@ -122,6 +173,39 @@ describe Ability do
122
173
 
123
174
  it { should_not be_able_to(:discover, asset) }
124
175
  it { should_not be_able_to(:read, asset) }
176
+ it { should_not be_able_to(:download, asset) }
177
+ end
178
+
179
+ context "Then a different registered user" do
180
+ let(:user) { create(:user) }
181
+ subject { ability }
182
+
183
+ it { should_not be_able_to(:discover, asset) }
184
+ it { should_not be_able_to(:read, asset) }
185
+ it { should_not be_able_to(:download, asset) }
186
+ end
187
+
188
+ context "Then that user" do
189
+ let(:user) { user_with_access }
190
+ subject { ability }
191
+
192
+ it { should be_able_to(:discover, asset) }
193
+ it { should be_able_to(:read, asset) }
194
+ it { should_not be_able_to(:download, asset) }
195
+ end
196
+ end
197
+
198
+ describe "Given an asset to which a specific user has download access" do
199
+ let(:user_with_access) { create(:user) }
200
+ let(:asset) { SolrDocument.new(id: 'user_read', download_access_person_ssim: [user_with_access.email]) }
201
+
202
+ context "Then a not-signed-in user" do
203
+ let(:user) { nil }
204
+ subject { ability }
205
+
206
+ it { should_not be_able_to(:discover, asset) }
207
+ it { should_not be_able_to(:read, asset) }
208
+ it { should_not be_able_to(:download, asset) }
125
209
  end
126
210
 
127
211
  context "Then a different registered user" do
@@ -130,6 +214,7 @@ describe Ability do
130
214
 
131
215
  it { should_not be_able_to(:discover, asset) }
132
216
  it { should_not be_able_to(:read, asset) }
217
+ it { should_not be_able_to(:download, asset) }
133
218
  end
134
219
 
135
220
  context "Then that user" do
@@ -138,6 +223,7 @@ describe Ability do
138
223
 
139
224
  it { should be_able_to(:discover, asset) }
140
225
  it { should be_able_to(:read, asset) }
226
+ it { should be_able_to(:download, asset) }
141
227
  end
142
228
  end
143
229
 
@@ -33,7 +33,7 @@ describe Blacklight::AccessControls::Catalog do
33
33
  # to call "super" and then add more permissions checks
34
34
  # after that without having to re-fetch the document.
35
35
  it 'returns the permissions doc' do
36
- expect(subject).to be_a(Blacklight::AccessControls::PermissionsSolrDocument)
36
+ expect(subject).to be_a(SolrDocument)
37
37
  end
38
38
  end
39
39
  end
@@ -66,4 +66,30 @@ describe Blacklight::AccessControls::Config do
66
66
  end
67
67
  end
68
68
 
69
+ describe '#download_group_field' do
70
+ subject { config.download_group_field }
71
+
72
+ it 'has a default value' do
73
+ expect(subject).to eq "download_access_group_ssim"
74
+ end
75
+
76
+ it 'can be set to a non-default value' do
77
+ config.download_group_field = 'something else'
78
+ expect(subject).to eq 'something else'
79
+ end
80
+ end
81
+
82
+ describe '#download_user_field' do
83
+ subject { config.download_user_field }
84
+
85
+ it 'has a default value' do
86
+ expect(subject).to eq "download_access_person_ssim"
87
+ end
88
+
89
+ it 'can be set to a non-default value' do
90
+ config.download_user_field = 'something else'
91
+ expect(subject).to eq 'something else'
92
+ end
93
+ end
94
+
69
95
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blacklight-access_controls
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Beer
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-12-01 00:00:00.000000000 Z
14
+ date: 2015-12-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: cancancan
@@ -146,7 +146,6 @@ files:
146
146
  - lib/blacklight/access_controls/enforcement.rb
147
147
  - lib/blacklight/access_controls/permissions_cache.rb
148
148
  - lib/blacklight/access_controls/permissions_query.rb
149
- - lib/blacklight/access_controls/permissions_solr_document.rb
150
149
  - lib/blacklight/access_controls/user.rb
151
150
  - lib/generators/blacklight/ability.rb
152
151
  - lib/generators/blacklight/access_controls_generator.rb
@@ -1,2 +0,0 @@
1
- class Blacklight::AccessControls::PermissionsSolrDocument < SolrDocument
2
- end