eaco 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/features/authorization_parse_error.feature +157 -0
  4. data/features/enterprise_authorization.feature +159 -0
  5. data/features/rails_integration.feature +1 -1
  6. data/features/role_based_authorization.feature +30 -7
  7. data/features/step_definitions/actor_steps.rb +29 -25
  8. data/features/step_definitions/enterprise_steps.rb +81 -0
  9. data/features/step_definitions/error_steps.rb +49 -0
  10. data/features/step_definitions/fixture_steps.rb +14 -0
  11. data/features/step_definitions/resource_steps.rb +16 -24
  12. data/features/support/env.rb +4 -2
  13. data/lib/eaco.rb +2 -0
  14. data/lib/eaco/actor.rb +4 -3
  15. data/lib/eaco/adapters/active_record.rb +1 -1
  16. data/lib/eaco/adapters/active_record/compatibility.rb +19 -14
  17. data/lib/eaco/adapters/active_record/compatibility/scoped.rb +25 -0
  18. data/lib/eaco/adapters/active_record/compatibility/v40.rb +11 -3
  19. data/lib/eaco/adapters/active_record/compatibility/v41.rb +14 -3
  20. data/lib/eaco/adapters/active_record/compatibility/v42.rb +10 -2
  21. data/lib/eaco/controller.rb +16 -3
  22. data/lib/eaco/coverage.rb +83 -0
  23. data/lib/eaco/cucumber/active_record.rb +13 -18
  24. data/lib/eaco/cucumber/active_record/department.rb +4 -0
  25. data/lib/eaco/cucumber/active_record/position.rb +2 -0
  26. data/lib/eaco/cucumber/active_record/schema.rb +20 -2
  27. data/lib/eaco/cucumber/active_record/user.rb +9 -0
  28. data/lib/eaco/cucumber/active_record/user/designators.rb +4 -1
  29. data/lib/eaco/cucumber/active_record/user/designators/authenticated.rb +54 -0
  30. data/lib/eaco/cucumber/active_record/user/designators/department.rb +58 -0
  31. data/lib/eaco/cucumber/active_record/user/designators/position.rb +53 -0
  32. data/lib/eaco/cucumber/active_record/user/designators/user.rb +4 -0
  33. data/lib/eaco/cucumber/world.rb +115 -5
  34. data/lib/eaco/designator.rb +7 -2
  35. data/lib/eaco/dsl.rb +9 -1
  36. data/lib/eaco/dsl/acl.rb +2 -2
  37. data/lib/eaco/dsl/actor.rb +6 -3
  38. data/lib/eaco/dsl/base.rb +5 -0
  39. data/lib/eaco/error.rb +10 -1
  40. data/lib/eaco/rake.rb +1 -0
  41. data/lib/eaco/rake/default_task.rb +29 -9
  42. data/lib/eaco/rake/utils.rb +38 -0
  43. data/lib/eaco/version.rb +1 -1
  44. data/spec/eaco/acl_spec.rb +34 -0
  45. data/spec/spec_helper.rb +3 -3
  46. metadata +18 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d92e2ed7aceab41d46ba1f57fa2b75f9bb1b298
4
- data.tar.gz: 32b88c52a4bf95262a6bd54524e60afa7ea802bd
3
+ metadata.gz: 2c8f2901fb8524328d3e14a6c08607c553912b7a
4
+ data.tar.gz: cf36cc16c76467699d29576720a06e1ca612663e
5
5
  SHA512:
6
- metadata.gz: 2212422e25d4927512160f445e0a25060bbc88bb95040ad99f6b747317a4c09f57f2e29822ab14bb5d9759ace82901b95f6d8abae52a88a7d6a8afab77c7533a
7
- data.tar.gz: 99f977abda6d1d732b9b804264d4691f01372e86bb0845a232b9c5a3f1fb344742e6c8bbe6ffd3985cb55e94558fa526752fa4a34ff6b40dec0dd0d8b06f972d
6
+ metadata.gz: 0bf06b2c70e7dda0a6790d89d7b5e23ce0ebe97e49026001bff0af29f69844eeb7a5b8c1f54f8d01151d86e7d4b4a9911301ef2edbdafac1958cccfdf6be3522
7
+ data.tar.gz: 2c1c4a9acc88bd4b814e99e1110cabe9e5f5622f29183a90c95180ba378887463091eec7d58dd88436cdb843f1e296b8cd5708adc82062ecf9d866375f22e8ae
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build Status](https://travis-ci.org/ifad/eaco.svg)](https://travis-ci.org/ifad/eaco)
4
4
  [![Coverage Status](https://coveralls.io/repos/ifad/eaco/badge.svg)](https://coveralls.io/r/ifad/eaco) (*currently writing specs*)
5
5
  [![Code Climate](https://codeclimate.com/github/ifad/eaco/badges/gpa.svg)](https://codeclimate.com/github/ifad/eaco)
6
- [![Inline docs](http://inch-ci.org/github/ifad/eaco.svg?branch=master)](http://inch-ci.org/github/ifad/eaco/master)
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)
8
8
 
9
9
  Eacus, the holder of the keys of Hades, is an ACL-based authorization
@@ -52,9 +52,9 @@ Create `config/authorization.rb` [(rdoc)](http://www.rubydoc.info/github/ifad/ea
52
52
  ```ruby
53
53
  # Defines `Document` to be an authorized resource.
54
54
  #
55
- # Adds Document.accessible_by and Document#allows
55
+ # Adds Document.accessible_by and Document#allows?
56
56
  #
57
- authorize Document, using: :lucene do
57
+ authorize Document, using: :pg_jsonb do
58
58
  roles :owner, :editor, :reader
59
59
 
60
60
  permissions do
@@ -0,0 +1,157 @@
1
+ Feature: Authorization rules error handling
2
+ When there's an error in the authorization rules,
3
+ it is reported in detail with a backtrace showing
4
+ where it happened.
5
+
6
+ Scenario: Giving rubbish
7
+ When I have a wrong authorization definition such as
8
+ """
9
+ if you give me rubbish please go elsewhere
10
+ """
11
+ Then I should receive a DSL error SyntaxError saying
12
+ """
13
+ \(feature\):1: syntax error.+please go elsewhere
14
+ """
15
+
16
+ Scenario: Referencing a non-existing model
17
+ When I have a wrong authorization definition such as
18
+ """
19
+ authorize ::Nonexistant, using: :pg_jsonb
20
+ """
21
+ Then I should receive a DSL error Eaco::Error saying
22
+ """
23
+ uninitialized constant Nonexistant
24
+ """
25
+
26
+ Scenario: Specifying an actor class with no Designators namespace
27
+ When I have a wrong authorization definition such as
28
+ """
29
+ class ::Foo
30
+ end
31
+
32
+ actor Foo do
33
+ designators do
34
+ frobber yay: true
35
+ end
36
+ end
37
+ """
38
+ Then I should receive a DSL error Eaco::Error saying
39
+ """
40
+ Please put designators implementations in Foo::Designators
41
+ """
42
+
43
+ Scenario: Specifing a non-existing designator implementation
44
+ When I have a wrong authorization definition on model User such as
45
+ """
46
+ actor $MODEL do
47
+ designators do
48
+ fropper from: :sgurtz
49
+ end
50
+ end
51
+ """
52
+ Then I should receive a DSL error Eaco::Error saying
53
+ """
54
+ Implementation .+User::Designators::Fropper for Designator fropper not found
55
+ """
56
+
57
+ Scenario: Badly specifying the designator options
58
+ When I have a wrong authorization definition on model User such as
59
+ """
60
+ actor $MODEL do
61
+ designators do
62
+ user on_the_rocks: true
63
+ end
64
+ end
65
+ """
66
+ Then I should receive a DSL error Eaco::Error saying
67
+ """
68
+ The designator option :from is required
69
+ """
70
+
71
+ Scenario: Badly specifying the permissions options
72
+ When I have a wrong authorization definition on model Document such as
73
+ """
74
+ authorize $MODEL do
75
+ permissions do
76
+ reader "Asdrubbale"
77
+ end
78
+ end
79
+ """
80
+ Then I should receive a DSL error Eaco::Error saying
81
+ """
82
+ Invalid reader permission definition: "Asdrubbale"
83
+ """
84
+
85
+ Scenario: Authorizing an Object with no known ORM
86
+ When I have a wrong authorization definition such as
87
+ """
88
+ class ::Foo
89
+ end
90
+
91
+ authorize Foo
92
+ """
93
+ Then I should receive a DSL error Eaco::Error saying
94
+ """
95
+ Don't know how to persist ACLs using <Foo>'s ORM
96
+ """
97
+
98
+ Scenario: Authorizing an Resource with no known .accessible_by
99
+ When I have a wrong authorization definition such as
100
+ """
101
+ class ::Bar
102
+ attr_accessor :acl
103
+ end
104
+
105
+ authorize Bar
106
+ """
107
+ Then I should receive a DSL error Eaco::Error saying
108
+ """
109
+ Don't know how to look up authorized records on <Bar>'s ORM
110
+ """
111
+
112
+ Scenario: Authorizing a Resource with a known ORM but without the acl field
113
+ When I have a wrong authorization definition on model Department such as
114
+ """
115
+ authorize $MODEL
116
+ """
117
+ Then I should receive a DSL error Eaco::Error saying
118
+ """
119
+ Please define a jsonb column named `acl` on .+Department
120
+ """
121
+
122
+ Scenario: Authorizing a Resource with a known ORM but unknown strategy
123
+ When I have a wrong authorization definition on model Document such as
124
+ """
125
+ authorize $MODEL
126
+ """
127
+ Then I should receive a DSL error Eaco::Error saying
128
+ """
129
+ .+Document.+ORM.+ActiveRecord::Base.+ use one of the available strategies: pg_jsonb
130
+ """
131
+
132
+ Scenario: Authorizing a Resource with the wrong ACL column type
133
+ When I have a wrong authorization definition such as
134
+ """
135
+ class ::Grabach < ActiveRecord::Base
136
+ connection.create_table 'grabaches' do |t|
137
+ t.string :acl
138
+ end
139
+ end
140
+
141
+ authorize ::Grabach
142
+ """
143
+ Then I should receive a DSL error Eaco::Error saying
144
+ """
145
+ The `acl` column on Grabach must be of the jsonb type
146
+ """
147
+
148
+ Scenario: Using an unsupported ActiveRecord version
149
+ When I am using ActiveRecord 3.0
150
+ And I have a wrong authorization definition on model Document such as
151
+ """
152
+ authorize $MODEL
153
+ """
154
+ Then I should receive a DSL error Eaco::Error saying
155
+ """
156
+ Unsupported Active Record version: 30
157
+ """
@@ -0,0 +1,159 @@
1
+ Feature: Role-based, flexible authorization
2
+ In an enterprise, rights might be granted to specific users, to any users,
3
+ or to specific departments or to specific positions in said departments.
4
+
5
+ Background:
6
+ Given I have an User actor defined as
7
+ """
8
+ actor $MODEL do
9
+ designators do
10
+ authenticated from: :class
11
+ user from: :id
12
+ position from: :position_ids
13
+ department from: :department_names
14
+ end
15
+ end
16
+ """
17
+ Given I have a Document resource defined as
18
+ """
19
+ authorize $MODEL, using: :pg_jsonb do
20
+ roles :writer, :reader
21
+
22
+ role :reader, "R/O"
23
+ role :writer, "R/W"
24
+
25
+ permissions do
26
+ reader :read
27
+ writer reader, :write
28
+ end
29
+ end
30
+ """
31
+
32
+ Given I have the following User records
33
+ | id | name |
34
+ | 1 | Dennis Ritchie |
35
+ | 2 | Rob Pike |
36
+ | 3 | William Gates |
37
+ | 4 | Steve Jobs |
38
+ | 5 | Tim Berners-Lee |
39
+
40
+ Given I have the following Department records
41
+ | id | name |
42
+ | 1 | ICT |
43
+ | 2 | BAR |
44
+ | 3 | COM |
45
+
46
+ Given I have the following Position records
47
+ | id | name | department_id | user_id |
48
+ | 1 | Director | 1 | 1 |
49
+ | 2 | Systems Analyst | 1 | 2 |
50
+ | 3 | Bartender | 2 | 3 |
51
+ | 4 | Director | 3 | 4 |
52
+ | 5 | Social Media Manager | 3 | 5 |
53
+
54
+ Given I have the following Document records
55
+ | name | acl |
56
+ | ICT Status Report | {"department:ICT":"reader", "position:1":"writer"} |
57
+ | ICT Budget Report | {"position:1":"writer"} |
58
+ | Cafeteria Menu | {"position:3":"writer", "authenticated:Eaco::Cucumber::ActiveRecord::User":"reader"} |
59
+ | Tim's Web Project | {"user:5":"writer", "position:2":"reader"} |
60
+
61
+ Scenario: The Director can access confidential document
62
+ When I am "Dennis Ritchie"
63
+ Then I can read the Document "ICT Status Report" being a writer
64
+ And I can write the Document "ICT Budget Report" being a writer
65
+ And I can read the Document "Cafeteria Menu" being a reader
66
+ But I can not read the Document "Tim's Web Project"
67
+ When I ask for Documents I can access, I get
68
+ | ICT Status Report |
69
+ | ICT Budget Report |
70
+ | Cafeteria Menu |
71
+
72
+ Scenario: Rob can see Tim's document
73
+ When I am "Rob Pike"
74
+ Then I can read the Documents "ICT Status Report, Tim's Web Project" being a reader
75
+ But I can not write the Document "Tim's Web Project" being a reader
76
+ When I ask for Documents I can access, I get
77
+ | ICT Status Report |
78
+ | Tim's Web Project |
79
+ | Cafeteria Menu |
80
+
81
+ Scenario: Tim can work on his project
82
+ When I am "Tim Berners-Lee"
83
+ Then I can not read the Document "ICT Status Report, ICT Budget Report"
84
+ And I can read the Document "Tim's Web Project" being a writer
85
+ And I can write the Document "Tim's Web Project" being a writer
86
+ When I ask for Documents I can access, I get
87
+ | Tim's Web Project |
88
+ | Cafeteria Menu |
89
+
90
+ Scenario: Bill is maintaining the Cafeteria Menu
91
+ When I am "William Gates"
92
+ Then I can not read the Documents "ICT Status Report, ICT Budget Report, Tim's Web Project"
93
+ But I can write the Document "Cafeteria Menu" being a writer
94
+ When I ask for Documents I can access, I get
95
+ | Cafeteria Menu |
96
+
97
+ Scenario: Steve can just read the menu
98
+ When I am "Steve Jobs"
99
+ Then I can not read the Documents "ICT Status Report, ICT Budget Report, Tim's Web Project"
100
+ And I can not write the Document "Cafeteria Menu" being a reader
101
+ But I can read the Document "Cafeteria Menu" being a reader
102
+ When I ask for Documents I can access, I get
103
+ | Cafeteria Menu |
104
+
105
+ Scenario: Resolving a specific user
106
+ When I parse the Designator "user:4"
107
+ Then it should describe itself as "User 'Steve Jobs'"
108
+ And it should have a label of "User"
109
+ And it should resolve itself to
110
+ | Steve Jobs |
111
+
112
+ Scenario: Resolving the ICT Director
113
+ When I make a Designator with "position" and "1"
114
+ Then it should describe itself as "Director in ICT"
115
+ And it should have a label of "Position"
116
+ And it should resolve itself to
117
+ | Dennis Ritchie |
118
+
119
+ Scenario: Resolving the ICT Department
120
+ When I parse the Designator "department:ICT"
121
+ Then it should describe itself as "ICT"
122
+ And it should have a label of "Department"
123
+ And it should resolve itself to
124
+ | Dennis Ritchie |
125
+ | Rob Pike |
126
+
127
+ Scenario: Resolving all authenticated users
128
+ When I make a Designator with "authenticated" and "Eaco::Cucumber::ActiveRecord::User"
129
+ Then it should describe itself as "Any authenticated user"
130
+ And it should have a label of "Any user"
131
+ And it should resolve itself to
132
+ | Dennis Ritchie |
133
+ | Rob Pike |
134
+ | William Gates |
135
+ | Steve Jobs |
136
+ | Tim Berners-Lee |
137
+
138
+ Scenario: Resolving different designators
139
+ When I have the following designators
140
+ | department:ICT |
141
+ | position:3 |
142
+ | user:1 |
143
+ Then they should resolve to
144
+ | Dennis Ritchie |
145
+ | Rob Pike |
146
+ | William Gates |
147
+
148
+ Scenario: Resolving an invalid designator
149
+ When I parse the invalid Designator "foo:on the rocks"
150
+ Then I should receive a Designator error Eaco::Error saying
151
+ """
152
+ Designator not found: "foo"
153
+ """
154
+
155
+ Scenario: Obtaining labels for roles
156
+ When I ask the Document the list of roles and labels
157
+ Then I should get the following roles and labels
158
+ | writer | R/W |
159
+ | reader | R/O |
@@ -8,4 +8,4 @@ Feature: Rails integration
8
8
  """
9
9
 
10
10
  Scenario:
11
- Then I should be able to set an ACL on it
11
+ Then I should be able to set an ACL on the Document
@@ -18,25 +18,48 @@ Feature: Role-Based authorization
18
18
  And I have an User actor defined as
19
19
  """
20
20
  actor $MODEL do
21
+ admin do |user|
22
+ user.admin?
23
+ end
24
+
21
25
  designators do
22
26
  user from: :id
23
27
  end
24
28
  end
25
29
  """
26
- Given I have an actor named Bob
27
- And I have an actor named Tom
30
+ Given I have an User actor named "Bob"
31
+ And I have an User actor named "Tom"
28
32
 
29
33
  Scenario: Discretionary access to a Resource
30
34
  When I have a confidential Document named "Supa Dupa Fly"
31
- And I grant Bob access to "Supa Dupa Fly" as a reader in quality of user
32
- Then Bob should be able to read "Supa Dupa Fly"
33
- And Tom should not be able to read "Supa Dupa Fly"
35
+ And I grant Bob access to Document "Supa Dupa Fly" as a reader in quality of user
36
+ Then Bob should be able to read Document "Supa Dupa Fly"
37
+ But Bob should not be able to write Document "Supa Dupa Fly"
38
+ And Tom should not be able to read Document "Supa Dupa Fly"
39
+ But I revoke Bob access to Document "Supa Dupa Fly" in quality of user
40
+ Then Bob should not be able to read Document "Supa Dupa Fly"
34
41
 
35
42
  Scenario: Extraction of accessible Resources
36
43
  When I have a confidential Document named "Strategic Plan"
37
- And I grant Bob access to "Strategic Plan" as a reader in quality of user
44
+ And I grant Bob access to Document "Strategic Plan" as a reader in quality of user
38
45
  And I have a confidential Document named "For Tom"
39
- And I grant Tom access to "For Tom" as a reader in quality of user
46
+ And I grant Tom access to Document "For Tom" as a reader in quality of user
40
47
  And I have a confidential Document named "For no one"
41
48
  Then Bob can see only "Strategic Plan" in the Document authorized list
42
49
  And Tom can see only "For Tom" in the Document authorized list
50
+
51
+ Scenario: Admin can see everything
52
+ When I have an admin User actor named "Boss"
53
+ And I have a confidential Document named "For Bob"
54
+ And I grant Bob access to Document "For Bob" as a reader in quality of user
55
+ And I have a confidential Document named "For no one"
56
+ Then Bob can see only "For Bob" in the Document authorized list
57
+ But Boss can see "For Bob, For no one" in the Document authorized list
58
+
59
+ Scenario: Handling invalid roles
60
+ When I have a confidential Document named "Foo Bar"
61
+ And I grant Bob access to Document "Foo Bar" as an invalid role frupper in quality of zomg
62
+ Then I should receive a Resource error Eaco::Error saying
63
+ """
64
+ The `frupper' role is not valid for .+Document' objects. Valid roles are: `reader, writer'
65
+ """
@@ -1,37 +1,41 @@
1
- Given(/I have an (\w+) actor defined as/) do |model_name, author_definition|
2
- @actor_model = find_model(model_name)
3
-
4
- eval_dsl author_definition, @actor_model
1
+ Given(/I have an (\w+) actor defined as/) do |model_name, actor_definition|
2
+ authorize_model model_name, actor_definition
5
3
  end
6
4
 
7
- Given(/I have an actor named (\w+)/) do |actor_name|
8
- actor = @actor_model.new
9
- actor.name = actor_name
10
- actor.save!
5
+ Given(/I have an (\w+) actor named "(.+?)"/) do |model_name, actor_name|
6
+ register_actor model_name, actor_name
7
+ end
11
8
 
12
- @actors ||= {}
13
- @actors[actor_name] = actor
9
+ Given(/I have an admin (\w+) actor named "(.+?)"/) do |model_name, actor_name|
10
+ register_actor model_name, actor_name, admin: true
14
11
  end
15
12
 
16
- When(/I grant (\w+) access to "(.+?)" as a (\w+) in quality of (\w+)/) do |actor_name, resource_name, role_name, designator|
17
- actor = @actors.fetch(actor_name)
18
- @resources[resource_name].grant role_name, designator, actor
19
- @resources[resource_name].save!
13
+ When(/I grant (\w+) access to (\w+) "(.+?)" as a (\w+) in quality of (\w+)/) do |actor_name, resource_model, resource_name, role_name, designator|
14
+ actor = fetch_actor(actor_name)
15
+ resource = fetch_resource(resource_model, resource_name)
16
+
17
+ resource.grant role_name, designator, actor
18
+ resource.save!
20
19
  end
21
20
 
22
- Then(/^(\w+) should be able to (\w+) "(.+?)"$/) do |actor_name, permission_name, resource_name|
23
- actor = @actors.fetch(actor_name)
24
- resource = @resources.fetch(resource_name)
21
+ When(/I revoke (\w+) access to (\w+) "(.+?)" in quality of (\w+)/) do |actor_name, resource_model, resource_name, designator|
22
+ actor = fetch_actor(actor_name)
23
+ resource = fetch_resource(resource_model, resource_name)
25
24
 
26
- unless actor.can? permission_name, resource
27
- raise "Expected #{actor_name} to be able to #{permission_name} #{resource_name}"
28
- end
25
+ resource.revoke designator, actor
26
+ resource.save!
29
27
  end
30
28
 
31
- Then(/^(\w+) should not be able to (\w+) "(.+?)"$/) do |actor_name, permission_name, resource_name|
32
- actor = @actors.fetch(actor_name)
33
- unless actor.cannot? permission_name, @resources.fetch(resource_name)
34
- raise "Expected #{actor_name} to not be able to #{permission_name} #{resource_name}"
35
- end
29
+ Then(/^(\w+) should be able to (\w+) (\w+) "(.+?)"$/) do |actor_name, permission_name, resource_model, resource_name|
30
+ actor = fetch_actor(actor_name)
31
+ resource = fetch_resource(resource_model, resource_name)
32
+
33
+ expect(actor.can?(permission_name, resource)).to be(true)
36
34
  end
37
35
 
36
+ Then(/^(\w+) should not be able to (\w+) (\w+) "(.+?)"$/) do |actor_name, permission_name, resource_model, resource_name|
37
+ actor = fetch_actor(actor_name)
38
+ resource = fetch_resource(resource_model, resource_name)
39
+
40
+ expect(actor.cannot?(permission_name, resource)).to be(true)
41
+ end