bali 2.0.0 → 2.1.1
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 +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
|
[ ](https://codeship.com/projects/95727)
|
4
4
|
|
5
|
+
[](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
|