blacklight-access_controls 0.1.0

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.
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