bali 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ module Bali::Judger
2
+ class NegativeJudge < Judge
3
+ def initialize
4
+ super(false)
5
+ end
6
+
7
+ def auth_level
8
+ :cannot
9
+ end
10
+
11
+ def reverse_auth_level
12
+ :can
13
+ end
14
+
15
+ def zeus_return_value
16
+ BALI_FALSE
17
+ end
18
+
19
+ def plant_return_value
20
+ BALI_TRUE
21
+ end
22
+
23
+ def default_positive_return_value
24
+ BALI_TRUE
25
+ end
26
+
27
+ def default_negative_fuzy_return_value
28
+ BALI_FUZY_TRUE
29
+ end
30
+
31
+ # cannot? by default return true when
32
+ def natural_value
33
+ BALI_TRUE
34
+ end
35
+
36
+ def need_to_check_for_intervention?
37
+ rule_group && rule_group.plant?
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Bali::Judger
2
+ class PositiveJudge < Judge
3
+ def initialize
4
+ super(false)
5
+ end
6
+
7
+ def auth_level
8
+ :can
9
+ end
10
+
11
+ def reverse_auth_level
12
+ :cannot
13
+ end
14
+
15
+ def zeus_return_value
16
+ BALI_TRUE
17
+ end
18
+
19
+ def plant_return_value
20
+ BALI_FALSE
21
+ end
22
+
23
+ def default_positive_return_value
24
+ BALI_TRUE
25
+ end
26
+
27
+ def default_negative_fuzy_return_value
28
+ BALI_FUZY_FALSE
29
+ end
30
+
31
+ # value that is natural to be returned by the jury
32
+ # can? by default return false
33
+ def natural_value
34
+ BALI_FALSE
35
+ end
36
+
37
+ def need_to_check_for_intervention?
38
+ rule_group && rule_group.zeus?
39
+ end
40
+ end # positive judger
41
+ end # module judger
@@ -0,0 +1,61 @@
1
+ class Bali::RoleExtractor
2
+ attr_reader :arg
3
+
4
+ # argument can be anything, as long as role extractor know how to extract
5
+ def initialize(arg)
6
+ @arg = arg
7
+ end
8
+
9
+ def get_roles(object = @arg)
10
+ case object
11
+ when String; get_role_string(object)
12
+ when Symbol; get_role_symbol(object)
13
+ when NilClass; get_role_nil(object)
14
+ when Array; get_role_array(object)
15
+ else
16
+ get_role_object(object)
17
+ end
18
+ end
19
+
20
+ private
21
+ def get_role_string(object)
22
+ [object]
23
+ end
24
+
25
+ def get_role_symbol(object)
26
+ [object]
27
+ end
28
+
29
+ def get_role_nil(object)
30
+ [object]
31
+ end
32
+
33
+ def get_role_array(object)
34
+ object
35
+ end
36
+
37
+ # this case, _subtarget_roles is an object but not a symbol or a string
38
+ # let's try to deduct subtarget's roles
39
+ def get_role_object(object)
40
+ object_class = object.class.to_s
41
+
42
+ # variable to hold deducted role of the passed object
43
+ deducted_roles = nil
44
+ role_extracted = false
45
+
46
+ Bali::TRANSLATED_SUBTARGET_ROLES.each do |current_subtarget_class, roles_field_name|
47
+ if object_class == current_subtarget_class
48
+ deducted_roles = object.send(roles_field_name)
49
+ deducted_roles = get_roles(deducted_roles)
50
+ role_extracted = true
51
+ break
52
+ end # if matching class
53
+ end # each TRANSLATED_SUBTARGET_ROLES
54
+
55
+ unless role_extracted
56
+ raise Bali::AuthorizationError, "Bali does not know how to process roles: #{object}"
57
+ end
58
+
59
+ deducted_roles
60
+ end
61
+ end
@@ -27,9 +27,10 @@ class Bali::RuleClass
27
27
  if rule_group.is_a?(Bali::RuleGroup)
28
28
  target_user = rule_group.subtarget
29
29
  if target_user == "__*__" || target_user == :"__*__"
30
- raise Bali::DslError, "__*__ is a reserved subtarget used by Bali's internal"
30
+ self.others_rule_group = rule_group
31
+ else
32
+ self.rule_groups[rule_group.subtarget] = rule_group
31
33
  end
32
- self.rule_groups[rule_group.subtarget] = rule_group
33
34
  else
34
35
  raise Bali::DslError, "Rule group must be an instance of Bali::RuleGroup, got: #{rule_group.class.name}"
35
36
  end
@@ -33,6 +33,9 @@ class Bali::RuleGroup
33
33
  self.cants = {}
34
34
 
35
35
  # by default, rule group is neither zeus nor plant
36
+ # it is neutral.
37
+ # meaning, it is neither allowed to do everything, nor it is
38
+ # prohibited to do anything. neutral.
36
39
  self.zeus = false
37
40
  self.plant = false
38
41
  end
data/lib/bali/objector.rb CHANGED
@@ -40,392 +40,82 @@ module Bali::Objector
40
40
  end
41
41
  end
42
42
 
43
+ # to allow class-level objection
43
44
  module Bali::Objector::Statics
44
- # FUZY-ed value is happen when it is not really clear, need further cross checking,
45
- # whether it is really allowed or not. It happens for example in block with others, such as this:
46
- #
47
- # describe :finance do
48
- # cannot :view
49
- # end
50
- # others do
51
- # can :view
52
- # can :index
53
- # end
54
- #
55
- # In the example above, objecting cannot view on finance will result in STRONG_FALSE, but
56
- # objecting can index on finance will result in FUZY_TRUE.
57
- #
58
- # Eventually, all FUZY value will be normal TRUE or FALSE if no definite counterpart
59
- # is found/defined
60
- BALI_FUZY_FALSE = -2
61
- BALI_FUZY_TRUE = 2
62
- BALI_FALSE = -1
63
- BALI_TRUE = 1
64
-
65
- # translate response for value above to traditional true/false
66
- def bali_translate_response(bali_bool_value)
67
- raise Bali::Error, "Expect bali value to be an integer" unless bali_bool_value.is_a?(Integer)
68
- if bali_bool_value < 0
69
- return false
70
- elsif bali_bool_value > 0
71
- return true
72
- else
73
- raise Bali::Error, "Bali bool value can either be negative or positive integer"
74
- end
45
+ # get the proper roles for the subtarget, for any type of subtarget
46
+ def bali_translate_subtarget_roles(arg)
47
+ role_extractor = Bali::RoleExtractor.new(arg)
48
+ role_extractor.get_roles
75
49
  end
76
50
 
77
- # will return array
78
- def bali_translate_subtarget_roles(_subtarget_roles)
79
- if _subtarget_roles.is_a?(String) || _subtarget_roles.is_a?(Symbol) || _subtarget_roles.is_a?(NilClass)
80
- return [_subtarget_roles]
81
- elsif _subtarget_roles.is_a?(Array)
82
- return _subtarget_roles
83
- else
84
- # this case, _subtarget_roles is an object but not a symbol or a string
85
- # let's try to deduct subtarget's roles
86
-
87
- _subtarget = _subtarget_roles
88
- _subtarget_class = _subtarget.class.to_s
89
-
90
- # variable to hold deducted role of the passed object
91
- deducted_roles = nil
92
-
93
- Bali::TRANSLATED_SUBTARGET_ROLES.each do |subtarget_class, roles_field_name|
94
- if _subtarget_class == subtarget_class
95
- deducted_roles = _subtarget.send(roles_field_name)
96
- if deducted_roles.is_a?(String) || deducted_roles.is_a?(Symbol) || deducted_roles.is_a?(NilClass)
97
- deducted_roles = [deducted_roles]
98
- break
99
- elsif deducted_roles.is_a?(Array)
100
- break
101
- end
102
- end # if matching class
103
- end # each TRANSLATED_SUBTARGET_ROLES
104
-
105
- if deducted_roles.nil?
106
- raise Bali::AuthorizationError, "Bali does not know how to process roles: #{_subtarget_roles}"
107
- end
108
-
109
- return deducted_roles
110
- end # if
111
- end
112
-
113
- ### options passable to bali_can? and bali_cannot? are:
114
- ### cross_action: if set to true wouldn't call its counterpart so as to prevent
115
- ### overflowing stack
116
- ### original_subtarget: the original passed to can? and cannot? before
117
- ### processed by bali_translate_subtarget_roles
118
-
119
- def bali_can?(subtarget, operation, record = self, options = {})
120
- # if performed on a class-level, don't call its class or it will return
121
- # Class. That's not what is expected.
122
- if self.is_a?(Class)
123
- klass = self
124
- else
125
- klass = self.class
126
- end
127
-
128
- rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
129
- other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")
130
-
131
- rule = nil
132
-
133
- # default of can? is false whenever RuleClass for that class is undefined
134
- # or RuleGroup for that subtarget is not defined
135
- if rule_group.nil?
136
- # no more chance for checking
137
- return BALI_FALSE if other_rule_group.nil?
138
- else
139
- # get the specific rule from its own describe block
140
- rule = rule_group.get_rule(:can, operation)
141
- end
142
-
143
- # retrieve rule from others group
144
- otherly_rule = other_rule_group.get_rule(:can, operation)
145
-
146
- # godly subtarget is allowed to do as he wishes
147
- # so long that the rule is not specificly defined
148
- # or overwritten by subsequent rule
149
- if rule_group && rule_group.zeus?
150
- if rule.nil?
151
- _options = options.dup
152
- _options[:cross_check] = true
153
- _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
154
-
155
- check_val = self.bali_cannot?(subtarget, operation, record, _options)
156
-
157
- # check further whether cant is defined to overwrite this can_all
158
- if check_val == BALI_TRUE
159
- return BALI_FALSE
160
- else
161
- return BALI_TRUE
162
- end
163
- end
164
- end
165
-
166
- # do after crosscheck
167
- # plan subtarget is not allowed unless spesificly defined
168
- return BALI_FALSE if rule_group && rule_group.plant? && rule.nil? && otherly_rule.nil?
169
-
170
- if rule.nil?
171
- # default if can? for undefined rule is false, after related clause
172
- # cannot be found in cannot?
173
-
174
- unless options[:cross_check]
175
- options[:cross_check] = true
176
- cross_check_value = self.bali_cannot?(subtarget, operation, record, options)
177
- end
178
-
179
- # either if rule from others block is defined, and the result so far is fuzy
180
- # or, otherly rule is defined, and it is still a cross check
181
- # plus, the result is not a definite BALI_TRUE/BALI_FALSE
182
- #
183
- # rationalisation:
184
- # 1. Definite answer such as BALI_TRUE and BALI_FALSE is to be prioritised over
185
- # FUZY answer, because definite answer is not gathered from others block where
186
- # FUZY answer is. Therefore, it is an intended result
187
- # 2. If the answer is FUZY, otherly_rule only be considered if the result
188
- # is either FUZY TRUE or FUZY FALSE, or
189
- # 3. Or, when already in cross check mode, we cannot retrieve cross_check_value
190
- # what we can is instead, if otherly rule is available, just to try the odd
191
- if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
192
- (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
193
- (otherly_rule && options[:cross_check] && cross_check_value.nil?)
194
- # give chance to check at the others block
195
- rule = otherly_rule
196
- else
197
- # either the return is not fuzy, or otherly rule is undefined
198
- if cross_check_value == BALI_TRUE
199
- return BALI_FALSE
200
- elsif cross_check_value == BALI_FALSE
201
- return BALI_TRUE
202
- end
203
- end
204
- end
205
-
206
- if rule
207
- if rule.has_decider?
208
- # must test first
209
- decider = rule.decider
210
- original_subtarget = options.fetch(:original_subtarget)
211
- case decider.arity
212
- when 0
213
- if rule.decider_type == :if
214
- return decider.() ? BALI_TRUE : BALI_FALSE
215
- elsif rule.decider_type == :unless
216
- unless decider.()
217
- return BALI_TRUE
218
- else
219
- return BALI_FALSE
220
- end
221
- end
222
- when 1
223
- if rule.decider_type == :if
224
- return decider.(record) ? BALI_TRUE : BALI_FALSE
225
- elsif rule.decider_type == :unless
226
- unless decider.(record)
227
- return BALI_TRUE
228
- else
229
- return BALI_FALSE
230
- end
231
- end
232
- when 2
233
- if rule.decider_type == :if
234
- return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
235
- elsif rule.decider_type == :unless
236
- unless decider.(record, original_subtarget)
237
- return BALI_TRUE
238
- else
239
- return BALI_FALSE
240
- end
241
- end
242
- end
243
- else
244
- # rule is properly defined
245
- return BALI_TRUE
246
- end
247
- end
248
-
249
- # return fuzy if otherly rule defines contrary to this (can)
250
- if other_rule_group.get_rule(:cannot, operation)
251
- return BALI_FUZY_FALSE
252
- else
253
- return BALI_FALSE
254
- end
255
- end
256
-
257
- def bali_cannot?(subtarget, operation, record = self, options = {})
258
- if self.is_a?(Class)
259
- klass = self
260
- else
261
- klass = self.class
262
- end
263
-
264
- rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
265
- other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")
266
-
267
- rule = nil
268
-
269
- # default of cannot? is true whenever RuleClass for that class is undefined
270
- # or RuleGroup for that subtarget is not defined
271
- if rule_group.nil?
272
- return BALI_TRUE if other_rule_group.nil?
273
- else
274
- # get the specific rule from its own describe block
275
- rule = rule_group.get_rule(:cannot, operation)
276
- end
277
-
278
- otherly_rule = other_rule_group.get_rule(:cannot, operation)
279
- # plant subtarget is not allowed to do things unless specificly defined
280
- if rule_group && rule_group.plant?
281
- if rule.nil?
282
- _options = options.dup
283
- _options[:cross_check] = true
284
- _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
285
-
286
- # check further whether defined in can?
287
- check_val = self.bali_can?(subtarget, operation, record, _options)
288
-
289
- if check_val == BALI_TRUE
290
- return BALI_FALSE # well, it is defined in can, so it must overwrite this cant_all rule
291
- else
292
- # plant, and then rule is not defined for further inspection. stright
293
- # is not allowed to do this thing
294
- return BALI_TRUE
295
- end
296
- end
297
- end
298
-
299
- if rule.nil?
300
- unless options[:cross_check]
301
- options[:cross_check] = true
302
- cross_check_value = self.bali_can?(subtarget, operation, record, options)
303
- end
304
-
305
- if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
306
- (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
307
- (otherly_rule && options[:cross_check] && cross_check_value.nil?)
308
- rule = otherly_rule
309
- else
310
- if cross_check_value == BALI_TRUE
311
- # from can? because of cross check, then it should be false
312
- return BALI_FALSE
313
- elsif cross_check_value == BALI_FALSE
314
- # from can? because of cross check returning false
315
- # then it should be true, that is, cannot
316
- return BALI_TRUE
317
- end
318
- end
319
- end
320
-
321
- # do after cross check
322
- # godly subtarget is not to be prohibited in his endeavours
323
- # so long that no specific rule about this operation is defined
324
- return BALI_FALSE if rule_group && rule_group.zeus? && rule.nil? && otherly_rule.nil?
325
-
326
- if rule
327
- if rule.has_decider?
328
- decider = rule.decider
329
- original_subtarget = options.fetch(:original_subtarget)
330
- case decider.arity
331
- when 0
332
- if rule.decider_type == :if
333
- return decider.() ? BALI_TRUE : BALI_FALSE
334
- elsif rule.decider_type == :unless
335
- unless decider.()
336
- return BALI_TRUE
337
- else
338
- return BALI_FALSE
339
- end
340
- end
341
- when 1
342
- if rule.decider_type == :if
343
- return decider.(record) ? BALI_TRUE : BALI_FALSE
344
- elsif rule.decider_type == :unless
345
- unless decider.(record)
346
- return BALI_TRUE
347
- else
348
- return BALI_FALSE
349
- end
350
- end
351
- when 2
352
- if rule.decider_type == :if
353
- return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
354
- elsif rule.decider_type == :unless
355
- unless decider.(record, original_subtarget)
356
- return BALI_TRUE
357
- else
358
- return BALI_FALSE
359
- end
360
- end
361
- end
362
- else
363
- return BALI_TRUE # rule is properly defined
364
- end # if rule has decider
365
- end # if rule is nil
366
-
367
- # return fuzy if otherly rule defines contrary to this cannot rule
368
- if other_rule_group.get_rule(:can, operation)
369
- return BALI_FUZY_TRUE
370
- else
371
- BALI_TRUE
372
- end
373
- end # bali cannot
374
-
375
51
  def can?(subtarget_roles, operation, record = self, options = {})
376
52
  subs = bali_translate_subtarget_roles(subtarget_roles)
377
53
  # well, it is largely not used unless decider's is 2 arity
378
- options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
54
+ original_subtarget = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
379
55
 
380
- can_value = BALI_FALSE
56
+ judgement_value = false
381
57
  role = nil
58
+ judger = nil
382
59
 
383
60
  subs.each do |subtarget|
384
- next if can_value == BALI_TRUE
61
+ next if judgement_value == true
62
+
63
+ judge = Bali::Judger::Judge.build(:can, {
64
+ subtarget: subtarget,
65
+ original_subtarget: original_subtarget,
66
+ operation: operation,
67
+ record: record
68
+ })
69
+ judgement_value = judge.judgement
70
+
385
71
  role = subtarget
386
- can_value = bali_can?(role, operation, record, options)
387
72
  end
388
73
 
389
- if can_value == BALI_FALSE && block_given?
390
- yield options[:original_subtarget], role, bali_translate_response(can_value)
74
+ if block_given?
75
+ yield original_subtarget, role, judgement_value
391
76
  end
392
- bali_translate_response can_value
77
+
78
+ judgement_value
393
79
  rescue => e
394
- if e.is_a?(Bali::AuthorizationError)
80
+ if e.is_a?(Bali::AuthorizationError) || e.is_a?(Bali::Error)
395
81
  raise e
396
82
  else
397
- raise Bali::ObjectionError, e.message
83
+ raise Bali::ObjectionError, e.message, e.backtrace
398
84
  end
399
85
  end
400
86
 
401
- def cant?(subtarget_roles, operation, record = self, options = {})
402
- puts "Deprecation Warning: please use cannot? instead, cant? will be deprecated on major release 3.0"
403
- cannot?(subtarget_roles, operation, record, options)
404
- end
405
-
406
87
  def cannot?(subtarget_roles, operation, record = self, options = {})
407
88
  subs = bali_translate_subtarget_roles subtarget_roles
408
- options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
89
+ original_subtarget = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]
409
90
 
410
- cant_value = BALI_TRUE
91
+ judgement_value = true
92
+ role = nil
93
+ judger = nil
411
94
 
412
95
  subs.each do |subtarget|
413
- next if cant_value == BALI_FALSE
414
- cant_value = bali_cannot?(subtarget, operation, record, options)
415
- if cant_value == BALI_FALSE
416
- role = subtarget
417
- if block_given?
418
- yield options[:original_subtarget], role, bali_translate_response(cant_value)
419
- end
420
- end
96
+ next if judgement_value == false
97
+
98
+ judge = Bali::Judger::Judge.build(:cannot, {
99
+ subtarget: subtarget,
100
+ original_subtarget: original_subtarget,
101
+ operation: operation,
102
+ record: record
103
+ })
104
+ judgement_value = judge.judgement
105
+
106
+ role = subtarget
107
+ end
108
+
109
+ if block_given?
110
+ yield original_subtarget, role, judgement_value
421
111
  end
422
112
 
423
- bali_translate_response cant_value
113
+ judgement_value
424
114
  rescue => e
425
115
  if e.is_a?(Bali::AuthorizationError)
426
116
  raise e
427
117
  else
428
- raise Bali::ObjectionError, e.message
118
+ raise Bali::ObjectionError, e.message, e.backtrace
429
119
  end
430
120
  end
431
121
 
@@ -444,8 +134,10 @@ module Bali::Objector::Statics
444
134
  end
445
135
 
446
136
  raise auth_error
447
- end
448
- end
137
+ else
138
+ return can_value
139
+ end # if cannot is false, means cannot
140
+ end # can?
449
141
  end
450
142
 
451
143
  def cant!(subtarget_roles, operation, record = self, options = {})
@@ -453,6 +145,11 @@ module Bali::Objector::Statics
453
145
  cannot!(subtarget_roles, operation, record, options)
454
146
  end
455
147
 
148
+ def cant?(subtarget_roles, operation)
149
+ puts "Deprecation Warning: please use cannot? instead, cant? will be deprecated on major release 3.0"
150
+ cannot?(subtarget_roles, operation)
151
+ end
152
+
456
153
  def cannot!(subtarget_roles, operation, record = self, options = {})
457
154
  cannot?(subtarget_roles, operation, record, options) do |original_subtarget, role, cant_value|
458
155
  if cant_value == false
@@ -468,7 +165,9 @@ module Bali::Objector::Statics
468
165
  end
469
166
 
470
167
  raise auth_error
471
- end
472
- end
168
+ else
169
+ return cant_value
170
+ end # if cannot is false, means can
171
+ end # cannot?
473
172
  end
474
173
  end