eaco 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/Guardfile +1 -1
- data/README.md +1 -1
- data/features/enterprise_authorization.feature +91 -0
- data/features/step_definitions/actor_steps.rb +8 -0
- data/features/step_definitions/controller_steps.rb +34 -0
- data/features/step_definitions/enterprise_steps.rb +30 -10
- data/lib/eaco/adapters/couchrest_model.rb +3 -0
- data/lib/eaco/adapters/couchrest_model/couchdb_lucene.rb +6 -3
- data/lib/eaco/controller.rb +3 -1
- data/lib/eaco/cucumber/world.rb +17 -1
- data/lib/eaco/designator.rb +4 -0
- data/lib/eaco/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9384ef5a98077f6557e1e69eb7f54fb0463d35d5
|
4
|
+
data.tar.gz: 5728d953f8b9aef185fc97a84ae1a236c35ae9eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f5c5de62937bef96163b634a895903cdc4fa76368ac7b1dc56765d395bb5445ca0c41e3ef9b49c6992b5dda264210ecb50b79613a574c31887ec7da83d35aec
|
7
|
+
data.tar.gz: 15d230d2416a2fd7a0ddaf5479433759d494d7a285944d38606f05630567a321f0baf4a787ecbd4a993ff155a9d1031a66c53fb97dbb9ba5876f9ba6b6801b47
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Eaco
|
2
2
|
|
3
3
|
[](https://travis-ci.org/ifad/eaco)
|
4
|
-
[](https://coveralls.io/r/ifad/eaco)
|
4
|
+
[](https://coveralls.io/r/ifad/eaco)
|
5
5
|
[](https://codeclimate.com/github/ifad/eaco)
|
6
6
|
[](http://inch-ci.org/github/ifad/eaco)
|
7
7
|
[](http://badge.fury.io/rb/eaco)
|
@@ -58,6 +58,25 @@ Feature: Role-based, flexible authorization
|
|
58
58
|
| Cafeteria Menu | {"position:3":"writer", "authenticated:Eaco::Cucumber::ActiveRecord::User":"reader"} |
|
59
59
|
| Tim's Web Project | {"user:5":"writer", "position:2":"reader"} |
|
60
60
|
|
61
|
+
Scenario: Granting access in batches
|
62
|
+
When I have a confidential Document named "For BAR and ICT"
|
63
|
+
And I grant access to Document "For BAR and ICT" to the following designators as reader
|
64
|
+
| department:BAR |
|
65
|
+
| department:ICT |
|
66
|
+
When I am "Dennis Ritchie"
|
67
|
+
Then I can read the Document "For BAR and ICT" being a reader
|
68
|
+
But I can not write the Document "For BAR and ICT" being a reader
|
69
|
+
When I am "Rob Pike"
|
70
|
+
Then I can read the Document "For BAR and ICT" being a reader
|
71
|
+
But I can not write the Document "For BAR and ICT" being a reader
|
72
|
+
When I am "William Gates"
|
73
|
+
Then I can read the Document "For BAR and ICT" being a reader
|
74
|
+
But I can not write the Document "For BAR and ICT" being a reader
|
75
|
+
When I am "Steve Jobs"
|
76
|
+
Then I can not read the Document "For BAR and ICT"
|
77
|
+
And I can not write the Document "For BAR and ICT"
|
78
|
+
|
79
|
+
|
61
80
|
Scenario: The Director can access confidential document
|
62
81
|
When I am "Dennis Ritchie"
|
63
82
|
Then I can read the Document "ICT Status Report" being a writer
|
@@ -106,6 +125,7 @@ Feature: Role-based, flexible authorization
|
|
106
125
|
When I parse the Designator "user:4"
|
107
126
|
Then it should describe itself as "User 'Steve Jobs'"
|
108
127
|
And it should have a label of "User"
|
128
|
+
And it should serialize to JSON as {"label": "User 'Steve Jobs'", "value": "user:4"}
|
109
129
|
And it should resolve itself to
|
110
130
|
| Steve Jobs |
|
111
131
|
|
@@ -113,6 +133,7 @@ Feature: Role-based, flexible authorization
|
|
113
133
|
When I make a Designator with "position" and "1"
|
114
134
|
Then it should describe itself as "Director in ICT"
|
115
135
|
And it should have a label of "Position"
|
136
|
+
And it should serialize to JSON as {"label": "Director in ICT", "value": "position:1"}
|
116
137
|
And it should resolve itself to
|
117
138
|
| Dennis Ritchie |
|
118
139
|
|
@@ -120,6 +141,7 @@ Feature: Role-based, flexible authorization
|
|
120
141
|
When I parse the Designator "department:ICT"
|
121
142
|
Then it should describe itself as "ICT"
|
122
143
|
And it should have a label of "Department"
|
144
|
+
And it should serialize to JSON as {"label": "ICT", "value": "department:ICT"}
|
123
145
|
And it should resolve itself to
|
124
146
|
| Dennis Ritchie |
|
125
147
|
| Rob Pike |
|
@@ -128,6 +150,7 @@ Feature: Role-based, flexible authorization
|
|
128
150
|
When I make a Designator with "authenticated" and "Eaco::Cucumber::ActiveRecord::User"
|
129
151
|
Then it should describe itself as "Any authenticated user"
|
130
152
|
And it should have a label of "Any user"
|
153
|
+
And it should serialize to JSON as {"label": "Any authenticated user", "value": "authenticated:Eaco::Cucumber::ActiveRecord::User"}
|
131
154
|
And it should resolve itself to
|
132
155
|
| Dennis Ritchie |
|
133
156
|
| Rob Pike |
|
@@ -152,8 +175,76 @@ Feature: Role-based, flexible authorization
|
|
152
175
|
Designator not found: "foo"
|
153
176
|
"""
|
154
177
|
|
178
|
+
Scenario: Obtaining the role of a valid designator
|
179
|
+
When I parse the Designator "department:ICT"
|
180
|
+
Then its role on the Document "ICT Status Report" should be reader
|
181
|
+
And its role on the Documents "Cafeteria Menu, ICT Budget Report, Tim's Web Project" should be nil
|
182
|
+
When I make a Designator with "position" and "1"
|
183
|
+
Then its role on the Documents "ICT Status Report, ICT Budget Report" should be writer
|
184
|
+
And its role on the Documents "Cafeteria Menu, Tim's Web Project" should be nil
|
185
|
+
|
186
|
+
Scenario: Obtaining the role of an invalid object
|
187
|
+
When I have a plain object as a Designator
|
188
|
+
Then its role on the Document "ICT Status Report" should give an Eaco::Error error saying
|
189
|
+
"""
|
190
|
+
role_of expects .+Object.+ to be a Designator or to .+respond_to.+:designators
|
191
|
+
"""
|
192
|
+
|
155
193
|
Scenario: Obtaining labels for roles
|
156
194
|
When I ask the Document the list of roles and labels
|
157
195
|
Then I should get the following roles and labels
|
158
196
|
| writer | R/W |
|
159
197
|
| reader | R/O |
|
198
|
+
|
199
|
+
Scenario: Authorizing a controller
|
200
|
+
When I have an authorized Controller defined as
|
201
|
+
"""
|
202
|
+
before_filter :find_document
|
203
|
+
|
204
|
+
authorize :show, [:document, :read ]
|
205
|
+
authorize :edit, [:document, :write]
|
206
|
+
|
207
|
+
def show
|
208
|
+
head :ok
|
209
|
+
end
|
210
|
+
|
211
|
+
def edit
|
212
|
+
head :ok
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def find_document
|
218
|
+
@document = Eaco::Cucumber::ActiveRecord::Document.where(name: params[:name]).first
|
219
|
+
end
|
220
|
+
"""
|
221
|
+
When I am "Dennis Ritchie"
|
222
|
+
And I invoke the Controller "show" action with query string "name=ICT Status Report"
|
223
|
+
Then the Controller should not raise an error
|
224
|
+
And I invoke the Controller "edit" action with query string "name=ICT Status Report"
|
225
|
+
Then the Controller should not raise an error
|
226
|
+
|
227
|
+
When I am "Rob Pike"
|
228
|
+
And I invoke the Controller "show" action with query string "name=ICT Status Report"
|
229
|
+
Then the Controller should not raise an error
|
230
|
+
And I invoke the Controller "edit" action with query string "name=ICT Status Report"
|
231
|
+
Then the Controller should raise an Eaco::Forbidden error saying
|
232
|
+
"""
|
233
|
+
User.+not authorized to `edit' on .+Document
|
234
|
+
"""
|
235
|
+
|
236
|
+
When I am "William Gates"
|
237
|
+
And I invoke the Controller "edit" action with query string "name=Cafeteria Menu"
|
238
|
+
Then the Controller should not raise an error
|
239
|
+
And I invoke the Controller "show" action with query string "name=ICT Status Report"
|
240
|
+
Then the Controller should raise an Eaco::Forbidden error saying
|
241
|
+
"""
|
242
|
+
User.+not authorized to `show' on .+Document
|
243
|
+
"""
|
244
|
+
|
245
|
+
When I am "Steve Jobs"
|
246
|
+
And I invoke the Controller "show" action with query string "name=One More Thing"
|
247
|
+
Then the Controller should raise an Eaco::Error error saying
|
248
|
+
"""
|
249
|
+
@document is not set, can't authorize .+show
|
250
|
+
"""
|
@@ -18,6 +18,14 @@ When(/I grant (\w+) access to (\w+) "(.+?)" as a (\w+) in quality of (\w+)/) do
|
|
18
18
|
resource.save!
|
19
19
|
end
|
20
20
|
|
21
|
+
When(/I grant access to (\w+) "(.+?)" to the following designators as (\w+)/) do |resource_model, resource_name, role, table|
|
22
|
+
resource = fetch_resource(resource_model, resource_name)
|
23
|
+
designators = table.raw.flatten.map {|d| Eaco::Designator.parse(d) }
|
24
|
+
|
25
|
+
resource.batch_grant role, designators
|
26
|
+
resource.save!
|
27
|
+
end
|
28
|
+
|
21
29
|
When(/I revoke (\w+) access to (\w+) "(.+?)" in quality of (\w+)/) do |actor_name, resource_model, resource_name, designator|
|
22
30
|
actor = fetch_actor(actor_name)
|
23
31
|
resource = fetch_resource(resource_model, resource_name)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
When(/I have an authorized Controller defined as/) do |controller_code|
|
2
|
+
require 'action_controller'
|
3
|
+
require 'eaco/controller'
|
4
|
+
|
5
|
+
@controller_class = Class.new(ActionController::Base)
|
6
|
+
@controller_class.send(:attr_accessor, :current_user)
|
7
|
+
@controller_class.instance_eval { include Eaco::Controller }
|
8
|
+
@controller_class.class_eval controller_code
|
9
|
+
end
|
10
|
+
|
11
|
+
When(/I invoke the Controller "(.+?)" action with query string "(.+?)"$/) do |action_name, query|
|
12
|
+
@controller = @controller_class.new
|
13
|
+
@action_name = action_name
|
14
|
+
|
15
|
+
@controller.current_user = @current_user
|
16
|
+
|
17
|
+
@controller.request = ActionDispatch::TestRequest.new('QUERY_STRING' => query).tap do |request|
|
18
|
+
request.params.update('action' => @action_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
@controller.response = ActionDispatch::TestResponse.new
|
22
|
+
end
|
23
|
+
|
24
|
+
Then(/the Controller should not raise an error/) do
|
25
|
+
expect { @controller.process @action_name }.to_not raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
Then(/the Controller should raise an (.+?) error saying/) do |error_class, error_contents|
|
29
|
+
error_class = error_class.constantize
|
30
|
+
|
31
|
+
expect { @controller.process @action_name }.to \
|
32
|
+
raise_error(error_class).
|
33
|
+
with_message(/#{error_contents}/)
|
34
|
+
end
|
@@ -5,22 +5,14 @@ When(/I am "(.+?)"$/) do |user_name|
|
|
5
5
|
end
|
6
6
|
|
7
7
|
Then(/I can (\w+) the Documents? "(.+?)" being a (\w+)$/) do |permission, document_names, role|
|
8
|
-
|
9
|
-
names = document_names.split(/,\s*/)
|
10
|
-
documents = model.where(name: names)
|
11
|
-
|
12
|
-
documents.each do |document|
|
8
|
+
check_documents document_names do |document|
|
13
9
|
expect(@current_user.can?(permission, document)).to eq(true)
|
14
10
|
expect(document.role_of(@current_user)).to eq(role.intern)
|
15
11
|
end
|
16
12
|
end
|
17
13
|
|
18
14
|
Then(/I can not (\w+) the Documents? "(.+?)" *(?:being a (\w+))?$/) do |permission, document_names, role|
|
19
|
-
|
20
|
-
names = document_names.split(/,\s*/)
|
21
|
-
documents = model.where(name: names)
|
22
|
-
|
23
|
-
documents.each do |document|
|
15
|
+
check_documents document_names do |document|
|
24
16
|
expect(@current_user.cannot?(permission, document)).to eq(true)
|
25
17
|
expect(document.role_of(@current_user)).to eq(role ? role.intern : nil)
|
26
18
|
end
|
@@ -41,10 +33,20 @@ When(/I parse the Designator "(.+?)"/) do |text|
|
|
41
33
|
@designator = Eaco::Designator.parse(text)
|
42
34
|
end
|
43
35
|
|
36
|
+
When(/I have a plain object as a Designator/) do
|
37
|
+
@designator = Object.new
|
38
|
+
end
|
39
|
+
|
44
40
|
Then(/it should describe itself as "(.+?)"/) do |description|
|
45
41
|
expect(@designator.describe).to eq(description)
|
46
42
|
end
|
47
43
|
|
44
|
+
Then(/it should serialize to JSON as (.+?)$/) do |json|
|
45
|
+
json = MultiJson.load(json).symbolize_keys
|
46
|
+
|
47
|
+
expect(@designator.as_json).to eq(json)
|
48
|
+
end
|
49
|
+
|
48
50
|
Then(/it should have a label of "(.+?)"/) do |label|
|
49
51
|
expect(@designator.label).to eq(label)
|
50
52
|
end
|
@@ -66,6 +68,24 @@ Then(/they should resolve to/) do |table|
|
|
66
68
|
expect(resolved.map(&:name)).to match_array(names)
|
67
69
|
end
|
68
70
|
|
71
|
+
Then(/its role on the Documents? "(.+?)" should be (\w+)/) do |document_names, role|
|
72
|
+
role = role == 'nil' ? nil : role.intern
|
73
|
+
|
74
|
+
check_documents document_names do |document|
|
75
|
+
expect(document.role_of(@designator)).to eq(role)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
Then(/its role on the Documents? "(.+?)" should give an (.+?) error saying/) do |document_names, error_class, error_contents|
|
80
|
+
error_class = error_class.constantize
|
81
|
+
|
82
|
+
check_documents document_names do |document|
|
83
|
+
expect { document.role_of(@designator) }.to \
|
84
|
+
raise_error(error_class).
|
85
|
+
with_message(/#{error_contents}/)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
69
89
|
When(/I ask the Document the list of roles and labels/) do
|
70
90
|
model = find_model('Document')
|
71
91
|
|
@@ -9,6 +9,8 @@ module Eaco
|
|
9
9
|
# @see ACL
|
10
10
|
# @see CouchDBLucene
|
11
11
|
#
|
12
|
+
# :nocov: because there are too many moving parts here and anyway we are
|
13
|
+
# going to deprecate this in favour of jsonb
|
12
14
|
module CouchrestModel
|
13
15
|
autoload :CouchDBLucene, 'eaco/adapters/couchrest_model/couchdb_lucene'
|
14
16
|
|
@@ -32,6 +34,7 @@ module Eaco
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
37
|
+
# :nocov:
|
35
38
|
|
36
39
|
end
|
37
40
|
end
|
@@ -48,6 +48,8 @@ module Eaco
|
|
48
48
|
# @see Actor
|
49
49
|
# @see Resource
|
50
50
|
#
|
51
|
+
# :nocov: because there are too many moving parts here and anyway we are
|
52
|
+
# going to deprecate this in favour of jsonb
|
51
53
|
module CouchDBLucene
|
52
54
|
|
53
55
|
##
|
@@ -58,13 +60,14 @@ module Eaco
|
|
58
60
|
# @return [CouchRest::Model::Search::View] the authorized collection scope.
|
59
61
|
#
|
60
62
|
def accessible_by(actor)
|
61
|
-
|
63
|
+
return search(nil) if actor.is_admin?
|
62
64
|
|
63
|
-
|
65
|
+
designators = actor.designators.map {|item| '"%s"' % item }
|
64
66
|
|
65
|
-
|
67
|
+
search "acl:(#{designators.join(' OR ')})"
|
66
68
|
end
|
67
69
|
end
|
70
|
+
# :nocov:
|
68
71
|
|
69
72
|
end
|
70
73
|
end
|
data/lib/eaco/controller.rb
CHANGED
@@ -2,7 +2,7 @@ begin
|
|
2
2
|
require 'active_support/concern'
|
3
3
|
rescue LoadError
|
4
4
|
# :nocov: This is falsely true during specs ran by Guard. FIXME.
|
5
|
-
abort 'Eaco::Controller requires
|
5
|
+
abort 'Eaco::Controller requires actioncontroller. Please add it to Gemfile.'
|
6
6
|
# :nocov:
|
7
7
|
end
|
8
8
|
|
@@ -87,6 +87,8 @@ module Eaco
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
protected
|
91
|
+
|
90
92
|
##
|
91
93
|
# Asks Eaco whether thou shalt pass or not.
|
92
94
|
#
|
data/lib/eaco/cucumber/world.rb
CHANGED
@@ -220,7 +220,7 @@ module Eaco
|
|
220
220
|
resources.fetch(model).fetch(name)
|
221
221
|
rescue KeyError
|
222
222
|
# :nocov:
|
223
|
-
raise "Resource #{model} '#{
|
223
|
+
raise "Resource #{model} '#{name}' not found in registry"
|
224
224
|
# :nocov:
|
225
225
|
end
|
226
226
|
|
@@ -243,6 +243,22 @@ module Eaco
|
|
243
243
|
@resources ||= {}
|
244
244
|
end
|
245
245
|
|
246
|
+
##
|
247
|
+
# Checks the given block on the given set of +Document+
|
248
|
+
#
|
249
|
+
# @param names [String] the document names, separated by +,+
|
250
|
+
# @param block [Proc] the code to run on each +Document+
|
251
|
+
#
|
252
|
+
# @return [void]
|
253
|
+
#
|
254
|
+
# @see Eaco::Cucumber::ActiveRecord::Document
|
255
|
+
#
|
256
|
+
def check_documents(names, &block)
|
257
|
+
model = find_model('Document')
|
258
|
+
names = names.split(/,\s*/)
|
259
|
+
model.where(name: names).each(&block)
|
260
|
+
end
|
261
|
+
|
246
262
|
##
|
247
263
|
# Returns a model in the {ActiveRecord} namespace.
|
248
264
|
#
|
data/lib/eaco/designator.rb
CHANGED
@@ -175,7 +175,9 @@ module Eaco
|
|
175
175
|
# @raise [NotImplementedError]
|
176
176
|
#
|
177
177
|
def search(query)
|
178
|
+
# :nocov:
|
178
179
|
raise NotImplementedError
|
180
|
+
# :nocov:
|
179
181
|
end
|
180
182
|
end
|
181
183
|
|
@@ -224,7 +226,9 @@ module Eaco
|
|
224
226
|
# @raise [NotImplementedError]
|
225
227
|
#
|
226
228
|
def resolve
|
229
|
+
# :nocov:
|
227
230
|
raise NotImplementedError
|
231
|
+
# :nocov:
|
228
232
|
end
|
229
233
|
|
230
234
|
##
|
data/lib/eaco/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eaco
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcello Barnaba
|
@@ -217,6 +217,7 @@ files:
|
|
217
217
|
- features/rails_integration.feature
|
218
218
|
- features/role_based_authorization.feature
|
219
219
|
- features/step_definitions/actor_steps.rb
|
220
|
+
- features/step_definitions/controller_steps.rb
|
220
221
|
- features/step_definitions/enterprise_steps.rb
|
221
222
|
- features/step_definitions/error_steps.rb
|
222
223
|
- features/step_definitions/fixture_steps.rb
|
@@ -319,6 +320,7 @@ test_files:
|
|
319
320
|
- features/rails_integration.feature
|
320
321
|
- features/role_based_authorization.feature
|
321
322
|
- features/step_definitions/actor_steps.rb
|
323
|
+
- features/step_definitions/controller_steps.rb
|
322
324
|
- features/step_definitions/enterprise_steps.rb
|
323
325
|
- features/step_definitions/error_steps.rb
|
324
326
|
- features/step_definitions/fixture_steps.rb
|