blacklight-access_controls 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/Gemfile +32 -0
  4. data/README.textile +74 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/blacklight-access_controls.gemspec +29 -0
  8. data/lib/blacklight-access_controls.rb +23 -0
  9. data/lib/blacklight/access_controls.rb +14 -0
  10. data/lib/blacklight/access_controls/ability.rb +148 -0
  11. data/lib/blacklight/access_controls/catalog.rb +27 -0
  12. data/lib/blacklight/access_controls/config.rb +39 -0
  13. data/lib/blacklight/access_controls/enforcement.rb +103 -0
  14. data/lib/blacklight/access_controls/permissions_cache.rb +19 -0
  15. data/lib/blacklight/access_controls/permissions_query.rb +53 -0
  16. data/lib/blacklight/access_controls/permissions_solr_document.rb +2 -0
  17. data/lib/blacklight/access_controls/user.rb +23 -0
  18. data/lib/generators/blacklight/ability.rb +4 -0
  19. data/lib/generators/blacklight/access_controls_generator.rb +49 -0
  20. data/solr_conf/conf/abc123 +0 -0
  21. data/solr_conf/conf/admin-extra.html +24 -0
  22. data/solr_conf/conf/admin-extra.menu-bottom.html +25 -0
  23. data/solr_conf/conf/admin-extra.menu-top.html +25 -0
  24. data/solr_conf/conf/clustering/carrot2/kmeans-attributes.xml +19 -0
  25. data/solr_conf/conf/clustering/carrot2/lingo-attributes.xml +24 -0
  26. data/solr_conf/conf/clustering/carrot2/stc-attributes.xml +19 -0
  27. data/solr_conf/conf/currency.xml +67 -0
  28. data/solr_conf/conf/dataimport.properties +3 -0
  29. data/solr_conf/conf/db-data-config.xml +93 -0
  30. data/solr_conf/conf/elevate.xml +38 -0
  31. data/solr_conf/conf/lang/contractions_ca.txt +8 -0
  32. data/solr_conf/conf/lang/contractions_fr.txt +15 -0
  33. data/solr_conf/conf/lang/contractions_ga.txt +5 -0
  34. data/solr_conf/conf/lang/contractions_it.txt +23 -0
  35. data/solr_conf/conf/lang/hyphenations_ga.txt +5 -0
  36. data/solr_conf/conf/lang/stemdict_nl.txt +6 -0
  37. data/solr_conf/conf/lang/stoptags_ja.txt +420 -0
  38. data/solr_conf/conf/lang/stopwords_ar.txt +125 -0
  39. data/solr_conf/conf/lang/stopwords_bg.txt +193 -0
  40. data/solr_conf/conf/lang/stopwords_ca.txt +220 -0
  41. data/solr_conf/conf/lang/stopwords_ckb.txt +136 -0
  42. data/solr_conf/conf/lang/stopwords_cz.txt +172 -0
  43. data/solr_conf/conf/lang/stopwords_da.txt +110 -0
  44. data/solr_conf/conf/lang/stopwords_de.txt +294 -0
  45. data/solr_conf/conf/lang/stopwords_el.txt +78 -0
  46. data/solr_conf/conf/lang/stopwords_en.txt +54 -0
  47. data/solr_conf/conf/lang/stopwords_es.txt +356 -0
  48. data/solr_conf/conf/lang/stopwords_eu.txt +99 -0
  49. data/solr_conf/conf/lang/stopwords_fa.txt +313 -0
  50. data/solr_conf/conf/lang/stopwords_fi.txt +97 -0
  51. data/solr_conf/conf/lang/stopwords_fr.txt +186 -0
  52. data/solr_conf/conf/lang/stopwords_ga.txt +110 -0
  53. data/solr_conf/conf/lang/stopwords_gl.txt +161 -0
  54. data/solr_conf/conf/lang/stopwords_hi.txt +235 -0
  55. data/solr_conf/conf/lang/stopwords_hu.txt +211 -0
  56. data/solr_conf/conf/lang/stopwords_hy.txt +46 -0
  57. data/solr_conf/conf/lang/stopwords_id.txt +359 -0
  58. data/solr_conf/conf/lang/stopwords_it.txt +303 -0
  59. data/solr_conf/conf/lang/stopwords_ja.txt +127 -0
  60. data/solr_conf/conf/lang/stopwords_lv.txt +172 -0
  61. data/solr_conf/conf/lang/stopwords_nl.txt +119 -0
  62. data/solr_conf/conf/lang/stopwords_no.txt +194 -0
  63. data/solr_conf/conf/lang/stopwords_pt.txt +253 -0
  64. data/solr_conf/conf/lang/stopwords_ro.txt +233 -0
  65. data/solr_conf/conf/lang/stopwords_ru.txt +243 -0
  66. data/solr_conf/conf/lang/stopwords_sv.txt +133 -0
  67. data/solr_conf/conf/lang/stopwords_th.txt +119 -0
  68. data/solr_conf/conf/lang/stopwords_tr.txt +212 -0
  69. data/solr_conf/conf/lang/userdict_ja.txt +29 -0
  70. data/solr_conf/conf/mapping-FoldToASCII.txt +3813 -0
  71. data/solr_conf/conf/mapping-ISOLatin1Accent.txt +246 -0
  72. data/solr_conf/conf/protwords.txt +21 -0
  73. data/solr_conf/conf/schema.blacklight.xml +724 -0
  74. data/solr_conf/conf/schema.xml +1268 -0
  75. data/solr_conf/conf/schema.xml.orig +1524 -0
  76. data/solr_conf/conf/solrconfig.adams.xml +1903 -0
  77. data/solr_conf/conf/solrconfig.blacklight.xml +411 -0
  78. data/solr_conf/conf/solrconfig.old.xml +1634 -0
  79. data/solr_conf/conf/solrconfig.xml +332 -0
  80. data/solr_conf/conf/solrconfig.xml.orig +3531 -0
  81. data/solr_conf/conf/spellings.txt +2 -0
  82. data/solr_conf/conf/stopwords.txt +14 -0
  83. data/solr_conf/conf/synonyms.txt +29 -0
  84. data/solr_conf/conf/update-script.js +53 -0
  85. data/solr_conf/conf/xslt/example.xsl +132 -0
  86. data/solr_conf/conf/xslt/example_atom.xsl +67 -0
  87. data/solr_conf/conf/xslt/example_rss.xsl +66 -0
  88. data/solr_conf/conf/xslt/luke.xsl +337 -0
  89. data/solr_conf/conf/xslt/updateXml.xsl +70 -0
  90. data/spec/factories/user.rb +6 -0
  91. data/spec/spec_helper.rb +29 -0
  92. data/spec/support/solr_support.rb +11 -0
  93. data/spec/test_app_templates/blacklight.yml +18 -0
  94. data/spec/test_app_templates/lib/generators/test_app_generator.rb +25 -0
  95. data/spec/unit/ability_spec.rb +202 -0
  96. data/spec/unit/catalog_spec.rb +41 -0
  97. data/spec/unit/config_spec.rb +69 -0
  98. data/spec/unit/enforcement_spec.rb +147 -0
  99. metadata +265 -0
@@ -0,0 +1,70 @@
1
+ <!--
2
+ * Licensed to the Apache Software Foundation (ASF) under one or more
3
+ * contributor license agreements. See the NOTICE file distributed with
4
+ * this work for additional information regarding copyright ownership.
5
+ * The ASF licenses this file to You under the Apache License, Version 2.0
6
+ * (the "License"); you may not use this file except in compliance with
7
+ * the License. You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ -->
17
+
18
+ <!--
19
+ Simple transform of Solr query response into Solr Update XML compliant XML.
20
+ When used in the xslt response writer you will get UpdaateXML as output.
21
+ But you can also store a query response XML to disk and feed this XML to
22
+ the XSLTUpdateRequestHandler to index the content. Provided as example only.
23
+ See http://wiki.apache.org/solr/XsltUpdateRequestHandler for more info
24
+ -->
25
+ <xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
26
+ <xsl:output media-type="text/xml" method="xml" indent="yes"/>
27
+
28
+ <xsl:template match='/'>
29
+ <add>
30
+ <xsl:apply-templates select="response/result/doc"/>
31
+ </add>
32
+ </xsl:template>
33
+
34
+ <!-- Ignore score (makes no sense to index) -->
35
+ <xsl:template match="doc/*[@name='score']" priority="100">
36
+ </xsl:template>
37
+
38
+ <xsl:template match="doc">
39
+ <xsl:variable name="pos" select="position()"/>
40
+ <doc>
41
+ <xsl:apply-templates>
42
+ <xsl:with-param name="pos"><xsl:value-of select="$pos"/></xsl:with-param>
43
+ </xsl:apply-templates>
44
+ </doc>
45
+ </xsl:template>
46
+
47
+ <!-- Flatten arrays to duplicate field lines -->
48
+ <xsl:template match="doc/arr" priority="100">
49
+ <xsl:variable name="fn" select="@name"/>
50
+
51
+ <xsl:for-each select="*">
52
+ <xsl:element name="field">
53
+ <xsl:attribute name="name"><xsl:value-of select="$fn"/></xsl:attribute>
54
+ <xsl:value-of select="."/>
55
+ </xsl:element>
56
+ </xsl:for-each>
57
+ </xsl:template>
58
+
59
+
60
+ <xsl:template match="doc/*">
61
+ <xsl:variable name="fn" select="@name"/>
62
+
63
+ <xsl:element name="field">
64
+ <xsl:attribute name="name"><xsl:value-of select="$fn"/></xsl:attribute>
65
+ <xsl:value-of select="."/>
66
+ </xsl:element>
67
+ </xsl:template>
68
+
69
+ <xsl:template match="*"/>
70
+ </xsl:stylesheet>
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ sequence(:email) { |n| "user_#{n}@example.com" }
4
+ password 'password'
5
+ end
6
+ end
@@ -0,0 +1,29 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+
3
+ require 'engine_cart'
4
+ EngineCart.load_application!
5
+
6
+ require 'blacklight-access_controls'
7
+ require 'factory_girl_rails'
8
+ require 'database_cleaner'
9
+
10
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
11
+
12
+ RSpec.configure do |config|
13
+ config.include FactoryGirl::Syntax::Methods
14
+
15
+ config.include SolrSupport
16
+
17
+ config.before(:suite) do
18
+ DatabaseCleaner.clean_with :truncation
19
+ end
20
+
21
+ config.before(:each) do
22
+ DatabaseCleaner.strategy = :transaction
23
+ DatabaseCleaner.start
24
+ end
25
+
26
+ config.after(:each) do
27
+ DatabaseCleaner.clean
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module SolrSupport
2
+
3
+ def create_solr_doc(hash)
4
+ doc = SolrDocument.new(hash)
5
+ solr = Blacklight.default_index.connection
6
+ solr.add(doc)
7
+ solr.commit
8
+ doc
9
+ end
10
+
11
+ end
@@ -0,0 +1,18 @@
1
+ # = jetty_path key
2
+ # each environment can have a jetty_path with absolute or relative
3
+ # (to app root) path to a jetty/solr install. This is used
4
+ # by the rake tasks that start up solr automatically for testing
5
+ # and by rake solr:marc:index.
6
+ #
7
+ # jetty_path is not used by a running Blacklight application
8
+ # at all. In general you do NOT need to deploy solr in Jetty, you can deploy it
9
+ # however you want.
10
+ # jetty_path is only required for rake tasks that need to know
11
+ # how to start up solr, generally for automated testing.
12
+
13
+ development:
14
+ adapter: solr
15
+ url: <%= ENV['SOLR_URL'] || "http://127.0.0.1:8983/solr/development" %>
16
+ test: &test
17
+ adapter: solr
18
+ url: <%= ENV['SOLR_URL'] || "http://127.0.0.1:8983/solr/test" %>
@@ -0,0 +1,25 @@
1
+ require 'rails/generators'
2
+
3
+ class TestAppGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../../../spec/test_app_templates", __FILE__)
5
+
6
+ # if you need to generate any additional configuration
7
+ # into the test app, this generator will be run immediately
8
+ # after setting up the application
9
+
10
+ def generate_blacklight
11
+ say_status('status', 'GENERATING BLACKLIGHT', :yellow)
12
+ generate "blacklight:install", "--devise"
13
+ end
14
+
15
+ def configure_blacklight
16
+ say_status('status', 'CONFIGURING BLACKLIGHT', :yellow)
17
+ remove_file 'config/blacklight.yml'
18
+ copy_file 'blacklight.yml', 'config/blacklight.yml'
19
+ end
20
+
21
+ def run_access_controls_generator
22
+ generate 'blacklight:access_controls'
23
+ end
24
+
25
+ end
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ require 'cancan/matchers'
3
+
4
+ describe Ability do
5
+ let(:ability) { Ability.new(user) }
6
+
7
+ describe "class methods" do
8
+ it 'has keys for access control fields' do
9
+ expect(Ability.read_group_field).to eq 'read_access_group_ssim'
10
+ expect(Ability.read_user_field).to eq 'read_access_person_ssim'
11
+ expect(Ability.discover_group_field).to eq 'discover_access_group_ssim'
12
+ expect(Ability.discover_user_field).to eq 'discover_access_person_ssim'
13
+ end
14
+ end
15
+
16
+ describe "Given an asset that has been made publicly discoverable" do
17
+ let(:asset) { SolrDocument.new(id: 'public_discovery',
18
+ discover_access_group_ssim: ['public']) }
19
+
20
+ context "Then a not-signed-in user" do
21
+ let(:user) { nil }
22
+ subject { ability }
23
+
24
+ it { should be_able_to(:discover, asset) }
25
+ it { should_not be_able_to(:read, asset) }
26
+ end
27
+
28
+ context "Then a registered user" do
29
+ let(:user) { create(:user) }
30
+ subject { ability }
31
+
32
+ it { should be_able_to(:discover, asset) }
33
+ it { should_not be_able_to(:read, asset) }
34
+ end
35
+
36
+ context 'With an ID instead of a SolrDocument' do
37
+ let(:user) { create(:user) }
38
+ subject { ability }
39
+
40
+ let(:asset) {
41
+ create_solr_doc(id: 'public_discovery',
42
+ discover_access_group_ssim: ['public'])
43
+ }
44
+
45
+ # It should still work, even if we just pass in an ID
46
+ it { should be_able_to(:discover, asset.id) }
47
+ it { should_not be_able_to(:read, asset.id) }
48
+ end
49
+ end
50
+
51
+ describe "Given an asset that has been made publicly readable" do
52
+ let(:asset) { SolrDocument.new(id: 'public_read',
53
+ read_access_group_ssim: ['public']) }
54
+
55
+ context "Then a not-signed-in user" do
56
+ let(:user) { nil }
57
+ subject { ability }
58
+
59
+ it { should be_able_to(:discover, asset) }
60
+ it { should be_able_to(:read, asset) }
61
+ end
62
+
63
+ context "Then a registered user" do
64
+ let(:user) { create(:user) }
65
+ subject { ability }
66
+
67
+ it { should be_able_to(:discover, asset) }
68
+ it { should be_able_to(:read, asset) }
69
+ end
70
+
71
+ context 'With an ID instead of a SolrDocument' do
72
+ let(:user) { create(:user) }
73
+ subject { ability }
74
+
75
+ let(:asset) {
76
+ create_solr_doc(id: 'public_read',
77
+ read_access_group_ssim: ['public'])
78
+ }
79
+
80
+ # It should still work, even if we just pass in an ID
81
+ it { should be_able_to(:discover, asset.id) }
82
+ it { should be_able_to(:read, asset.id) }
83
+ end
84
+ end
85
+
86
+ describe "Given an asset to which a specific user has discovery access" do
87
+ let(:user_with_access) { create(:user) }
88
+ let(:asset) { SolrDocument.new(id: 'user_disco', discover_access_person_ssim: [user_with_access.email]) }
89
+
90
+ context "Then a not-signed-in user" do
91
+ let(:user) { nil }
92
+ subject { ability }
93
+
94
+ it { should_not be_able_to(:discover, asset) }
95
+ it { should_not be_able_to(:read, asset) }
96
+ end
97
+
98
+ context "Then a different registered user" do
99
+ let(:user) { create(:user) }
100
+ subject { ability }
101
+
102
+ it { should_not be_able_to(:discover, asset) }
103
+ it { should_not be_able_to(:read, asset) }
104
+ end
105
+
106
+ context "Then that user" do
107
+ let(:user) { user_with_access }
108
+ subject { ability }
109
+
110
+ it { should be_able_to(:discover, asset) }
111
+ it { should_not be_able_to(:read, asset) }
112
+ end
113
+ end
114
+
115
+ describe "Given an asset to which a specific user has read access" do
116
+ let(:user_with_access) { create(:user) }
117
+ let(:asset) { SolrDocument.new(id: 'user_read', read_access_person_ssim: [user_with_access.email]) }
118
+
119
+ context "Then a not-signed-in user" do
120
+ let(:user) { nil }
121
+ subject { ability }
122
+
123
+ it { should_not be_able_to(:discover, asset) }
124
+ it { should_not be_able_to(:read, asset) }
125
+ end
126
+
127
+ context "Then a different registered user" do
128
+ let(:user) { create(:user) }
129
+ subject { ability }
130
+
131
+ it { should_not be_able_to(:discover, asset) }
132
+ it { should_not be_able_to(:read, asset) }
133
+ end
134
+
135
+ context "Then that user" do
136
+ let(:user) { user_with_access }
137
+ subject { ability }
138
+
139
+ it { should be_able_to(:discover, asset) }
140
+ it { should be_able_to(:read, asset) }
141
+ end
142
+ end
143
+
144
+
145
+ describe '.user_class' do
146
+ subject { Blacklight::AccessControls::Ability.user_class }
147
+ it { is_expected.to eq User }
148
+ end
149
+
150
+ describe '#guest_user' do
151
+ let(:user) { nil }
152
+ subject { ability.guest_user }
153
+
154
+ it 'is a new user' do
155
+ expect(subject).to be_a User
156
+ expect(subject.new_record?).to be_truthy
157
+ end
158
+ end
159
+
160
+ describe '#user_groups' do
161
+ subject { ability.user_groups }
162
+
163
+ context 'an unregistered user' do
164
+ let(:user) { build(:user) }
165
+ it { is_expected.to contain_exactly('public') }
166
+ end
167
+
168
+ context 'a registered user' do
169
+ let(:user) { create(:user) }
170
+ it { is_expected.to contain_exactly('registered', 'public') }
171
+ end
172
+
173
+ context 'a user with groups' do
174
+ let(:user) { double(groups: ['group1', 'group2'], new_record?: false) }
175
+ it { is_expected.to include('group1', 'group2') }
176
+ end
177
+ end
178
+
179
+ describe "with a custom method" do
180
+ let(:user) { create(:user) }
181
+ subject { MyAbility.new(user) }
182
+
183
+ before do
184
+ class MyAbility
185
+ include Blacklight::AccessControls::Ability
186
+ self.ability_logic +=[:setup_my_permissions]
187
+
188
+ def setup_my_permissions
189
+ can :accept, SolrDocument
190
+ end
191
+ end
192
+ end
193
+
194
+ after do
195
+ Object.send(:remove_const, :MyAbility)
196
+ end
197
+
198
+ # Make sure it called the custom method
199
+ it { should be_able_to(:accept, SolrDocument) }
200
+ end
201
+
202
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blacklight::AccessControls::Catalog do
4
+ let(:controller) { CatalogController.new }
5
+
6
+ describe '#enforce_show_permissions' do
7
+ subject { controller.send(:enforce_show_permissions) }
8
+ let(:params) {{ id: doc.id }}
9
+
10
+ before do
11
+ allow(controller).to receive(:current_user).and_return(user)
12
+ allow(controller).to receive(:params).and_return(params)
13
+ end
14
+
15
+ context 'when user is not logged in' do
16
+ let(:doc) { create_solr_doc(id: '123') }
17
+ let(:user) { User.new }
18
+
19
+ it 'denies access' do
20
+ expect { subject }.to raise_error(Blacklight::AccessControls::AccessDenied)
21
+ end
22
+ end
23
+
24
+ context 'when user has access' do
25
+ let(:doc) { create_solr_doc(id: '123', read_access_person_ssim: user.email) }
26
+ let(:user) { build(:user) }
27
+
28
+ it 'allows access' do
29
+ expect { subject }.to_not raise_error
30
+ end
31
+
32
+ # So that you can override enforce_show_permissions
33
+ # to call "super" and then add more permissions checks
34
+ # after that without having to re-fetch the document.
35
+ it 'returns the permissions doc' do
36
+ expect(subject).to be_a(Blacklight::AccessControls::PermissionsSolrDocument)
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe Blacklight::AccessControls::Config do
4
+ let(:config) { described_class.new }
5
+
6
+ describe '#user_model' do
7
+ it 'has a default value' do
8
+ expect(config.user_model).to eq 'User'
9
+ end
10
+
11
+ it 'can be set to a non-default value' do
12
+ config.user_model = 'Student'
13
+ expect(config.user_model).to eq 'Student'
14
+ end
15
+ end
16
+
17
+ describe '#discover_group_field' do
18
+ subject { config.discover_group_field }
19
+
20
+ it 'has a default value' do
21
+ expect(subject).to eq "discover_access_group_ssim"
22
+ end
23
+
24
+ it 'can be set to a non-default value' do
25
+ config.discover_group_field = 'something else'
26
+ expect(subject).to eq 'something else'
27
+ end
28
+ end
29
+
30
+ describe '#discover_user_field' do
31
+ subject { config.discover_user_field }
32
+
33
+ it 'has a default value' do
34
+ expect(subject).to eq "discover_access_person_ssim"
35
+ end
36
+
37
+ it 'can be set to a non-default value' do
38
+ config.discover_user_field = 'something else'
39
+ expect(subject).to eq 'something else'
40
+ end
41
+ end
42
+
43
+ describe '#read_group_field' do
44
+ subject { config.read_group_field }
45
+
46
+ it 'has a default value' do
47
+ expect(subject).to eq "read_access_group_ssim"
48
+ end
49
+
50
+ it 'can be set to a non-default value' do
51
+ config.read_group_field = 'something else'
52
+ expect(subject).to eq 'something else'
53
+ end
54
+ end
55
+
56
+ describe '#read_user_field' do
57
+ subject { config.read_user_field }
58
+
59
+ it 'has a default value' do
60
+ expect(subject).to eq "read_access_person_ssim"
61
+ end
62
+
63
+ it 'can be set to a non-default value' do
64
+ config.read_user_field = 'something else'
65
+ expect(subject).to eq 'something else'
66
+ end
67
+ end
68
+
69
+ end