bali 2.0.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/CHANGELOG.md +69 -0
- data/README.md +79 -221
- data/bin/console +0 -7
- data/lib/bali.rb +1 -8
- data/lib/bali/dsl/map_rules_dsl.rb +15 -2
- data/lib/bali/dsl/rules_for_dsl.rb +65 -27
- data/lib/bali/foundations/rule/rule.rb +9 -1
- data/lib/bali/foundations/rule/rule_class.rb +34 -3
- data/lib/bali/foundations/rule/rule_group.rb +21 -6
- data/lib/bali/integrators/rules_integrator.rb +3 -25
- data/lib/bali/objector.rb +196 -91
- data/lib/bali/printer.rb +63 -0
- data/lib/bali/version.rb +1 -1
- metadata +19 -18
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
OGE5M2M0ZmE1MGJmMDc4M2ZmM2Q3NzlkNGY3OTAxMDUzNGEwYTBiNg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmQ0MGNkOGRiMjJlZjc1YTFmZmMzN2FjNDNiNTU1N2I3OTZkZjMyYg==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NWUxZDA3YmFkMDI1YzIwMDUzMjdmNTIxNjBhZmZjNTZmNzc3NWUxMjNmNDU4
|
10
|
+
MmYzNDgxNzczZjc1ODcxMDU0N2FkN2U0ZDM0NDhmYzI1NzY5N2ZjZTAyZGY1
|
11
|
+
MTBiYTQ1ZjU2ZjhhNmUxNjZhZjJkYWM1NTk1Yzk4ZDAyNjM2OWM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NWUwZTM5NTA5NGFiZDI3NDgxMGM5YTlkYzM2Mzc0Y2NjM2IyMmZiYTNiODFh
|
14
|
+
NWMzMzMzZjgyY2QyZWM4MTc1YzdlNGI0NmE0YzM3YmZjNTg4M2I0OWFmZTAw
|
15
|
+
MWE0Mzk4OWY3YzI2NTVhZjIxOGNkYWRjOGE0NzhiMzYzZWQ2NjE=
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
## Changelog
|
2
|
+
|
3
|
+
== Version 1.0.0beta1
|
4
|
+
|
5
|
+
1. Initial version
|
6
|
+
|
7
|
+
== Version 1.0.0rc1
|
8
|
+
|
9
|
+
1. Fix bug where user can't check on class
|
10
|
+
2. Adding new clause: cant_all
|
11
|
+
|
12
|
+
== Version 1.0.0rc2
|
13
|
+
|
14
|
+
1. [Fix bug when class's name, as a constant, is reloaded](http://stackoverflow.com/questions/2509350/rails-class-object-id-changes-after-i-make-a-request) (re-allocated to different address in the memory)
|
15
|
+
2. Allow describing rule for `nil`, useful if user is not authenticated thus role is probably `nil`
|
16
|
+
3. Remove pry from development dependency
|
17
|
+
|
18
|
+
== Version 1.0.0rc3
|
19
|
+
|
20
|
+
1. Each target class should includes `Bali::Objector`, for the following reasons:
|
21
|
+
- Makes it clear that class do want to include the Bali::Objector
|
22
|
+
- Transparant, and thus less confusing as to where "can?" and "cant" come from
|
23
|
+
- When ruby re-parse the class's codes for any reasons, parser will be for sure include Bali::Objector
|
24
|
+
2. Return `true` to any `can?` for undefined target/subtarget alike
|
25
|
+
3. Return `false` to any `cant?` for undefined target/subtarget alike
|
26
|
+
|
27
|
+
== Version 1.0.0
|
28
|
+
|
29
|
+
1. Released the stable version of this gem
|
30
|
+
|
31
|
+
== Version 1.1.0rc1
|
32
|
+
|
33
|
+
1. Ability for rule class to be parsed later by passing `later: true` to rule class definition
|
34
|
+
2. Add `Bali.parse` and `Bali.parse!` (`Bali.parse!` executes "later"-tagged rule class, Bali.parse executes automatically after all rules are defined)
|
35
|
+
3. Added more thorough testing specs
|
36
|
+
4. Proc can be served under `unless` for defining the rule's decider
|
37
|
+
|
38
|
+
== Version 1.1.0rc2
|
39
|
+
|
40
|
+
1. Ability to check `can?` and `cant?` for subtarget with multiple roles
|
41
|
+
2. Describe multiple rules at once for multiple subtarget
|
42
|
+
|
43
|
+
== Version 1.2.0
|
44
|
+
|
45
|
+
1. Passing real object as subtarget's role, instead of symbol or array of symbol
|
46
|
+
2. Clause can also yielding user, along with the object in question
|
47
|
+
|
48
|
+
== Version 2.0.0rc1
|
49
|
+
|
50
|
+
1. `Bali::AuthorizationError` subclass of `StandardError` tailored for raising error regarding with authorisation
|
51
|
+
2. Deprecating `cant`, `cant?`, and `cant_all` in favor of `cannot`, `cannot?` and `cannot_all`
|
52
|
+
3. new objectors `can!` and `cannot!` to raise error on inappropriate access
|
53
|
+
|
54
|
+
== Version 2.0.0
|
55
|
+
|
56
|
+
1. Release!
|
57
|
+
|
58
|
+
== Version 2.1.0
|
59
|
+
|
60
|
+
1. `others` block would allow for rule definitions within it to be applied for all undefined subtargets of a target
|
61
|
+
2. Fixing bug when roles_for of a user object retrieves `nil` as the user's role, it won't acknowledge that the user is indeed having `nil`-role and raising an error instead.
|
62
|
+
3. Inherits rules by passing `:inherits` option when defining `rules_for`
|
63
|
+
4. `clear_rules` within `describe` or `others` block to remove all inherited rules (or any rules previously defined) for that subtarget
|
64
|
+
5. Adding `Bali::Printer` that would enable for rules to be printed by calling `.pretty_print` on it
|
65
|
+
|
66
|
+
== Version 2.1.1
|
67
|
+
|
68
|
+
1. Bug fixes on `clear_rules` which it clear rules defined in `others` even when not asked to
|
69
|
+
2. Bug fixes on `Bali::Printer` where inherited rules print the wrong target class due to another bug in an internal file (but doesn't hamper rules-checking logic)
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
[ ![Codeship Status for saveav/bali](https://codeship.com/projects/d2f3ded0-20cf-0133-e425-0eade5a669ff/status?branch=release)](https://codeship.com/projects/95727)
|
4
4
|
|
5
|
+
[![Code Climate](https://codeclimate.com/github/saveav/bali/badges/gpa.svg)](https://codeclimate.com/github/saveav/bali)
|
6
|
+
|
5
7
|
Bali is a powerful, framework-agnostic, thread-safe Ruby language authorization library. It is a universal authorization library, in the sense that it does not assume you to use specific Ruby library/gem/framework in order for successful use of this gem.
|
6
8
|
|
7
9
|
Bali is short for Bulwark Authorization Library.
|
@@ -29,49 +31,7 @@ And then execute:
|
|
29
31
|
|
30
32
|
## Usage
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
Rule in Bali is the law determining whether a user (called `subtarget`) can do or perform a specific operation on a target (which is your resource/model).
|
35
|
-
|
36
|
-
```ruby
|
37
|
-
Bali.map_rules do
|
38
|
-
rules_for My::Transaction, as: :transaction do
|
39
|
-
describe(:supreme_user) { can_all }
|
40
|
-
describe :admin_user do
|
41
|
-
can_all
|
42
|
-
# a more specific rule would be executed even if can_all is present
|
43
|
-
can :cancel,
|
44
|
-
if: proc { |record| record.payment_channel == "CREDIT_CARD" &&
|
45
|
-
!record.is_settled? }
|
46
|
-
end
|
47
|
-
describe "general user", can: [:update, :edit], cant: [:delete]
|
48
|
-
describe "finance user" do
|
49
|
-
can :update, :delete, :edit
|
50
|
-
can :delete, if: proc { |record| record.is_settled? }
|
51
|
-
can :cancel, unless: proc { |record| record.is_settled? }
|
52
|
-
end # finance_user description
|
53
|
-
describe :guest { cant_all }
|
54
|
-
describe nil { cant_all }
|
55
|
-
end # rules_for
|
56
|
-
end
|
57
|
-
```
|
58
|
-
|
59
|
-
You may or may not assign an alias name (`as`). Make sure to keep it unique had you decided to give alias name to your rules group.
|
60
|
-
|
61
|
-
It is also possible for a rule to be defined for multiple subtarget at once:
|
62
|
-
|
63
|
-
```ruby
|
64
|
-
Bali.map_rules do
|
65
|
-
rules_for My::Transaction do
|
66
|
-
# rules described bellow will affect both :general_user and :finance_user
|
67
|
-
describe :general_user, :finance_user do
|
68
|
-
can :update, :edit
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
```
|
73
|
-
|
74
|
-
### Can and Cant? testing
|
34
|
+
Please access [wiki pages](https://github.com/saveav/bali/wiki) for a more detailed, guided explanation, and see what Bali can do. This usage is a simple demonstration what average, standard use of Bali would looks like.
|
75
35
|
|
76
36
|
Say:
|
77
37
|
|
@@ -96,143 +56,94 @@ class My::Employee
|
|
96
56
|
end
|
97
57
|
```
|
98
58
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
transaction
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
transaction
|
110
|
-
transaction
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
As we have never define the `rules_for` My::Employee before, any attempt to `can?` for `My::Employee` will return `false`, so does any attempt to object `cant?` on which will only return `true` for any given subtarget and operation.
|
128
|
-
|
129
|
-
### Can and Cant testing with multiple-roles subtarget
|
130
|
-
|
131
|
-
A subtarget may have multiple roles. For eg., a user may have a role of `finance_user` and `general_user`. A general user normally by itself cannot `delete`, or `cancel`; but a `finance_user` does can, so long the condition is met. But, if a subtarget has role of both `finance_user` and `general_user`, he/she can perform `delete` or `cancel` (so far that the condition is met.)
|
132
|
-
|
133
|
-
Thus, if we have:
|
134
|
-
|
135
|
-
```ruby
|
136
|
-
txn = My::Transaction.new
|
137
|
-
txn.process_transaction(from_user_input)
|
138
|
-
|
139
|
-
# delete or cancel can only happen when a transaction is settled
|
140
|
-
# as per rule definition
|
141
|
-
txn.is_settled = true
|
142
|
-
txn.save
|
143
|
-
|
144
|
-
subtarget = User.new
|
145
|
-
subtarget.roles = [:finance_user, :general_user]
|
146
|
-
|
147
|
-
txn.can?(subtarget.roles, :delete) # => true
|
148
|
-
txn.cant?(subtarget.roles, :delete) # => false
|
149
|
-
txn.can?(:general_user, :delete) # => false
|
150
|
-
```
|
151
|
-
|
152
|
-
That is, we can check `can?` and `cant?` with multiple roles by passing array of roles to it.
|
153
|
-
|
154
|
-
### Using subtarget's instance for Can and Cant testing
|
155
|
-
|
156
|
-
You may pass in real subtarget instance rather than (1) a symbol, (2) string or (3) array of string/symbol for can/cant testing.
|
157
|
-
|
158
|
-
In order to do so, you need to specify the field/method in the subtarget that will be used to evaluating the subtarget's role(s). To do that, we define `roles_for` as follow inside `Bali.map_rules` block:
|
159
|
-
|
160
|
-
```ruby
|
161
|
-
Bali.map_rules do
|
162
|
-
roles_for My::Employee, :roles
|
163
|
-
roles_for My::AdminUser, :admin_roles
|
164
|
-
roles_for My::BankUser, :roles
|
165
|
-
|
166
|
-
# rules definition
|
167
|
-
# may follow
|
168
|
-
end
|
169
|
-
```
|
170
|
-
|
171
|
-
`roles_for` accept two parameters, namely the class of the subtarget, and the field/method that will be invoked on it to gain data about its role(s).
|
172
|
-
|
173
|
-
By doing so, we can now perform authorisation testing as follow:
|
59
|
+
Your task is to define rule, with context to `My::Transaction` object, in which:
|
60
|
+
|
61
|
+
1. Supreme user can do everything
|
62
|
+
2. Admin user can do everything, but:
|
63
|
+
- Can only cancel transaction if the transaction is done using credit card, and the transaction itself is not settled yet
|
64
|
+
3. General user can:
|
65
|
+
- Download transaction
|
66
|
+
4. Finance user can:
|
67
|
+
- Index transaction
|
68
|
+
- Download transaction
|
69
|
+
- Delete transaction if the transaction is settled
|
70
|
+
- Cancel transaction if the transaction is settled
|
71
|
+
5. Monitoring user can:
|
72
|
+
- Index transaction
|
73
|
+
- Download transaction
|
74
|
+
6. Sales team can:
|
75
|
+
- Index transaction
|
76
|
+
- Download transaction
|
77
|
+
6. Unlogged in user can:
|
78
|
+
- Index transaction
|
79
|
+
- Download transaction
|
80
|
+
- Report fraud
|
81
|
+
7. Guest user can:
|
82
|
+
- Index transaction
|
83
|
+
- Download transaction
|
84
|
+
- Report fraud
|
85
|
+
|
86
|
+
The specification above seems very terrifying, but with Bali, those can be defined in a succinct way, as follow:
|
174
87
|
|
175
88
|
```ruby
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
1. Zero arity
|
202
|
-
2. One arity
|
203
|
-
3. Two arity
|
204
|
-
|
205
|
-
When rule is very brief, use zero-arity rule clause as below:
|
206
|
-
|
207
|
-
```ruby
|
208
|
-
Bali.map_rules do
|
209
|
-
rules_for My::Transaction do
|
210
|
-
describe(:staff) { can :cancel }
|
211
|
-
describe(:finance) { can :cancel }
|
89
|
+
Bali.map_rules do
|
90
|
+
rules_for My::Transaction do
|
91
|
+
describe(:supreme_user) { can_all }
|
92
|
+
describe :admin_user do
|
93
|
+
can_all
|
94
|
+
# a more specific rule would be executed even if can_all is present
|
95
|
+
can :cancel,
|
96
|
+
if: proc { |record| record.payment_channel == "CREDIT_CARD" &&
|
97
|
+
!record.is_settled? }
|
98
|
+
end
|
99
|
+
describe "general user", can: [:download]
|
100
|
+
describe "finance" do
|
101
|
+
can :delete, if: proc { |record| record.is_settled? }
|
102
|
+
can :cancel, unless: proc { |record| record.is_settled? }
|
103
|
+
end # finance_user description
|
104
|
+
describe :guest, nil { can :report_fraud }
|
105
|
+
describe :client do
|
106
|
+
can :create
|
107
|
+
end
|
108
|
+
others do
|
109
|
+
cannot_all
|
110
|
+
can :download, :index
|
111
|
+
cannot :create
|
112
|
+
end
|
113
|
+
end # rules_for
|
212
114
|
end
|
213
|
-
end
|
214
115
|
```
|
215
116
|
|
216
|
-
|
117
|
+
## Can and Cant? testing
|
118
|
+
|
119
|
+
Assuming that there exist a variable `transaction` which is an instance of `My::Transaction`:
|
217
120
|
|
218
121
|
```ruby
|
219
|
-
|
220
|
-
|
221
|
-
|
122
|
+
transaction.cant?(:general_user, :delete) # => false
|
123
|
+
transaction.can("general user", :download) # => true
|
124
|
+
transaction.can?(:finance, :delete) # depends on context
|
125
|
+
transaction.can?(:monitoring, :index) # => true
|
126
|
+
transaction.can?(:sales, :download) # => true
|
127
|
+
transaction.can?(:admin_user, :cancel) # depends on context
|
128
|
+
transaction.can?(:supreme_user, :cancel) # => true
|
129
|
+
transaction.can?(:guest, :download) # => false
|
130
|
+
transaction.can?(nil, :download) # => true
|
131
|
+
transaction.can?(nil, :report_fraud) # => true
|
132
|
+
transaction.can?(:undefined_subtarget, :see) # => false
|
133
|
+
transaction.cant?(:undefined_subtarget, :index) # => true
|
134
|
+
transaction.can?(:client, :create) # => true
|
135
|
+
transaction.can?(:finance, :create) # => false
|
136
|
+
transaction.can?(:admin, :create) # => true
|
222
137
|
```
|
223
138
|
|
224
|
-
|
225
|
-
|
226
|
-
In order to do that, we need to resort to 2-arity decider, as follow:
|
139
|
+
Rule can also be tested on a class:
|
227
140
|
|
228
141
|
```ruby
|
229
|
-
|
230
|
-
|
231
|
-
|
142
|
+
My::Transaction.can?(:client, :create) # => true
|
143
|
+
My::Transaction.can?(:guest, :create) # => false
|
144
|
+
My::Employee.can?(:undefined_subtarget, :create) # => false
|
232
145
|
```
|
233
146
|
|
234
|
-
This way, we can keep our controller/logic/model clean from unnecessary and un-DRY role-testing logic.
|
235
|
-
|
236
147
|
## Contributing
|
237
148
|
|
238
149
|
Bug reports and pull requests are welcome on GitHub at https://github.com/saveav/bali. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
@@ -243,57 +154,4 @@ Bali is proudly available as open source under the terms of the [MIT License](ht
|
|
243
154
|
|
244
155
|
## Changelog
|
245
156
|
|
246
|
-
|
247
|
-
|
248
|
-
1. Initial version
|
249
|
-
|
250
|
-
== Version 1.0.0rc1
|
251
|
-
|
252
|
-
1. Fix bug where user can't check on class
|
253
|
-
2. Adding new clause: cant_all
|
254
|
-
|
255
|
-
== Version 1.0.0rc2
|
256
|
-
|
257
|
-
1. [Fix bug when class's name, as a constant, is reloaded](http://stackoverflow.com/questions/2509350/rails-class-object-id-changes-after-i-make-a-request) (re-allocated to different address in the memory)
|
258
|
-
2. Allow describing rule for `nil`, useful if user is not authenticated thus role is probably `nil`
|
259
|
-
3. Remove pry from development dependency
|
260
|
-
|
261
|
-
== Version 1.0.0rc3
|
262
|
-
|
263
|
-
1. Each target class should includes `Bali::Objector`, for the following reasons:
|
264
|
-
- Makes it clear that class do want to include the Bali::Objector
|
265
|
-
- Transparant, and thus less confusing as to where "can?" and "cant" come from
|
266
|
-
- When ruby re-parse the class's codes for any reasons, parser will be for sure include Bali::Objector
|
267
|
-
2. Return `true` to any `can?` for undefined target/subtarget alike
|
268
|
-
3. Return `false` to any `cant?` for undefined target/subtarget alike
|
269
|
-
|
270
|
-
== Version 1.0.0
|
271
|
-
|
272
|
-
1. Released the stable version of this gem
|
273
|
-
|
274
|
-
== Version 1.1.0rc1
|
275
|
-
|
276
|
-
1. Ability for rule class to be parsed later by passing `later: true` to rule class definition
|
277
|
-
2. Add `Bali.parse` and `Bali.parse!` (`Bali.parse!` executes "later"-tagged rule class, Bali.parse executes automatically after all rules are defined)
|
278
|
-
3. Added more thorough testing specs
|
279
|
-
4. Proc can be served under `unless` for defining the rule's decider
|
280
|
-
|
281
|
-
== Version 1.1.0rc2
|
282
|
-
|
283
|
-
1. Ability to check `can?` and `cant?` for subtarget with multiple roles
|
284
|
-
2. Describe multiple rules at once for multiple subtarget
|
285
|
-
|
286
|
-
== Version 1.2.0
|
287
|
-
|
288
|
-
1. Passing real object as subtarget's role, instead of symbol or array of symbol
|
289
|
-
2. Clause can also yielding user, along with the object in question
|
290
|
-
|
291
|
-
== Version 2.0.0rc1
|
292
|
-
|
293
|
-
1. `Bali::AuthorizationError` subclass of `StandardError` tailored for raising error regarding with authorisation
|
294
|
-
2. Deprecating `cant`, `cant?`, and `cant_all` in favor of `cannot`, `cannot?` and `cannot_all`
|
295
|
-
3. new objectors `can!` and `cannot!` to raise error on inappropriate access
|
296
|
-
|
297
|
-
== Version 2.0.0
|
298
|
-
|
299
|
-
1. Release!
|
157
|
+
Please refer to CHANGELOG.md to see it
|
data/bin/console
CHANGED
@@ -3,12 +3,5 @@
|
|
3
3
|
require "bundler/setup"
|
4
4
|
require "bali"
|
5
5
|
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
6
|
require "irb"
|
14
7
|
IRB.start
|
data/lib/bali.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative "bali/dsl/map_rules_dsl"
|
|
5
5
|
require_relative "bali/dsl/rules_for_dsl"
|
6
6
|
|
7
7
|
require_relative "bali/objector"
|
8
|
+
require_relative "bali/printer"
|
8
9
|
|
9
10
|
require_relative "bali/foundations/all_foundations"
|
10
11
|
require_relative "bali/integrators/all_integrators"
|
@@ -13,12 +14,6 @@ module Bali
|
|
13
14
|
# mapping class to a RuleClass
|
14
15
|
RULE_CLASS_MAP = {}
|
15
16
|
|
16
|
-
# from symbol to full class name
|
17
|
-
ALIASED_RULE_CLASS_MAP = {}
|
18
|
-
|
19
|
-
# from full class name to symbol
|
20
|
-
REVERSE_ALIASED_RULE_CLASS_MAP = {}
|
21
|
-
|
22
17
|
# {
|
23
18
|
# User: :roles,
|
24
19
|
# AdminUser: :admin_roles
|
@@ -38,8 +33,6 @@ module Bali
|
|
38
33
|
|
39
34
|
def clear_rules
|
40
35
|
Bali::RULE_CLASS_MAP.clear
|
41
|
-
Bali::REVERSE_ALIASED_RULE_CLASS_MAP.clear
|
42
|
-
Bali::ALIASED_RULE_CLASS_MAP.clear
|
43
36
|
true
|
44
37
|
end
|
45
38
|
end
|