eaco 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/ifad/eaco.svg)](https://travis-ci.org/ifad/eaco)
|
4
|
-
[![Coverage Status](https://coveralls.io/repos/ifad/eaco/badge.svg)](https://coveralls.io/r/ifad/eaco)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/ifad/eaco/badge.svg)](https://coveralls.io/r/ifad/eaco)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/ifad/eaco/badges/gpa.svg)](https://codeclimate.com/github/ifad/eaco)
|
6
6
|
[![Inline docs](http://inch-ci.org/github/ifad/eaco.svg?branch=master)](http://inch-ci.org/github/ifad/eaco)
|
7
7
|
[![Gem Version](https://badge.fury.io/rb/eaco.svg)](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
|