Module: Bali::Objector::Statics

Defined in:
lib/bali/objector.rb

Constant Summary collapse

BALI_FUZY_FALSE =

FUZY-ed value is happen when it is not really clear, need further cross checking, whether it is really allowed or not. It happens for example in block with others, such as this:

describe :finance do

cannot :view

end others do

can :view
can :index

end

In the example above, objecting cannot view on finance will result in STRONG_FALSE, but objecting can index on finance will result in FUZY_TRUE.

Eventually, all FUZY value will be normal TRUE or FALSE if no definite counterpart is found/defined

-2
BALI_FUZY_TRUE =
2
BALI_FALSE =
-1
BALI_TRUE =
1

Instance Method Summary collapse

Instance Method Details

#bali_can?(subtarget, operation, record = self, options = {}) ⇒ Boolean

options passable to bali_can? and bali_cannot? are: cross_action: if set to true wouldn’t call its counterpart so as to prevent

overflowing stack

original_subtarget: the original passed to can? and cannot? before

processed by bali_translate_subtarget_roles

Returns:

  • (Boolean)


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/bali/objector.rb', line 119

def bali_can?(subtarget, operation, record = self, options = {})
  # if performed on a class-level, don't call its class or it will return
  # Class. That's not what is expected.
  if self.is_a?(Class)
    klass = self
  else
    klass = self.class
  end

  rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
  other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__") 

  rule = nil 

  # default of can? is false whenever RuleClass for that class is undefined
  # or RuleGroup for that subtarget is not defined
  if rule_group.nil?
    # no more chance for checking
    return BALI_FALSE if other_rule_group.nil?
  else
    # get the specific rule from its own describe block
    rule = rule_group.get_rule(:can, operation)
  end

  # retrieve rule from others group
  otherly_rule = other_rule_group.get_rule(:can, operation)

  # godly subtarget is allowed to do as he wishes
  # so long that the rule is not specificly defined
  # or overwritten by subsequent rule
  if rule_group && rule_group.zeus?
    if rule.nil?
      _options = options.dup
      _options[:cross_check] = true
      _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?

      check_val = self.bali_cannot?(subtarget, operation, record, _options)

      # check further whether cant is defined to overwrite this can_all
      if check_val == BALI_TRUE 
        return BALI_FALSE
      else
        return BALI_TRUE
      end
    end
  end

  # do after crosscheck
  # plan subtarget is not allowed unless spesificly defined
  return BALI_FALSE if rule_group && rule_group.plant? && rule.nil? && otherly_rule.nil?

  if rule.nil?
    # default if can? for undefined rule is false, after related clause
    # cannot be found in cannot?

    unless options[:cross_check] 
      options[:cross_check] = true
      cross_check_value = self.bali_cannot?(subtarget, operation, record, options)
    end

    # either if rule from others block is defined, and the result so far is fuzy
    # or, otherly rule is defined, and it is still a cross check
    # plus, the result is not a definite BALI_TRUE/BALI_FALSE
    #
    # rationalisation:
    # 1. Definite answer such as BALI_TRUE and BALI_FALSE is to be prioritised over
    #    FUZY answer, because definite answer is not gathered from others block where
    #    FUZY answer is. Therefore, it is an intended result
    # 2. If the answer is FUZY, otherly_rule only be considered if the result 
    #    is either FUZY TRUE or FUZY FALSE, or
    # 3. Or, when already in cross check mode, we cannot retrieve cross_check_value
    #    what we can is instead, if otherly rule is available, just to try the odd 
    if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
        (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
        (otherly_rule && options[:cross_check] && cross_check_value.nil?)
      # give chance to check at the others block
      rule = otherly_rule
    else
      # either the return is not fuzy, or otherly rule is undefined
      if cross_check_value == BALI_TRUE
        return BALI_FALSE
      elsif cross_check_value == BALI_FALSE
        return BALI_TRUE
      end
    end
  end

  if rule
    if rule.has_decider?
      # must test first
      decider = rule.decider
      original_subtarget = options.fetch(:original_subtarget)
      case decider.arity
      when 0
        if rule.decider_type == :if
          return decider.() ? BALI_TRUE : BALI_FALSE
        elsif rule.decider_type == :unless
          unless decider.()
            return BALI_TRUE
          else
            return BALI_FALSE
          end
        end
      when 1
        if rule.decider_type == :if
          return decider.(record) ? BALI_TRUE : BALI_FALSE
        elsif rule.decider_type == :unless
          unless decider.(record)
            return BALI_TRUE
          else
            return BALI_FALSE
          end
        end
      when 2
        if rule.decider_type == :if
          return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
        elsif rule.decider_type == :unless
          unless decider.(record, original_subtarget)
            return BALI_TRUE
          else
            return BALI_FALSE
          end
        end
      end
    else
      # rule is properly defined
      return BALI_TRUE
    end
  end

  # return fuzy if otherly rule defines contrary to this (can)
  if other_rule_group.get_rule(:cannot, operation)
    return BALI_FUZY_FALSE
  else
    return BALI_FALSE
  end
end

#bali_cannot?(subtarget, operation, record = self, options = {}) ⇒ Boolean

Returns:

  • (Boolean)


257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/bali/objector.rb', line 257

def bali_cannot?(subtarget, operation, record = self, options = {})
  if self.is_a?(Class)
    klass = self
  else
    klass = self.class
  end

  rule_group = Bali::Integrators::Rule.rule_group_for(klass, subtarget)
  other_rule_group = Bali::Integrators::Rule.rule_group_for(klass, "__*__")

  rule = nil

  # default of cannot? is true whenever RuleClass for that class is undefined
  # or RuleGroup for that subtarget is not defined
  if rule_group.nil?
    return BALI_TRUE if other_rule_group.nil?
  else
    # get the specific rule from its own describe block
    rule = rule_group.get_rule(:cannot, operation)
  end

  otherly_rule = other_rule_group.get_rule(:cannot, operation)
  # plant subtarget is not allowed to do things unless specificly defined
  if rule_group && rule_group.plant?
    if rule.nil?
      _options = options.dup
      _options[:cross_check] = true
      _options[:original_subtarget] = original_subtarget if _options[:original_subtarget].nil?
      
      # check further whether defined in can?
      check_val = self.bali_can?(subtarget, operation, record, _options)

      if check_val == BALI_TRUE
        return BALI_FALSE # well, it is defined in can, so it must overwrite this cant_all rule
      else
        # plant, and then rule is not defined for further inspection. stright
        # is not allowed to do this thing
        return BALI_TRUE
      end
    end
  end

  if rule.nil?
    unless options[:cross_check]
      options[:cross_check] = true
      cross_check_value = self.bali_can?(subtarget, operation, record, options)
    end

    if (otherly_rule && cross_check_value && !(cross_check_value == BALI_TRUE || cross_check_value == BALI_FALSE)) ||
        (otherly_rule && (cross_check_value == BALI_FUZY_FALSE || cross_check_value == BALI_FUZY_TRUE)) ||
        (otherly_rule && options[:cross_check] && cross_check_value.nil?)
      rule = otherly_rule
    else
      if cross_check_value == BALI_TRUE
        # from can? because of cross check, then it should be false
        return BALI_FALSE
      elsif cross_check_value == BALI_FALSE
        # from can? because of cross check returning false
        # then it should be true, that is, cannot
        return BALI_TRUE
      end
    end
  end

  # do after cross check
  # godly subtarget is not to be prohibited in his endeavours
  # so long that no specific rule about this operation is defined
  return BALI_FALSE if rule_group && rule_group.zeus? && rule.nil? && otherly_rule.nil?

  if rule
    if rule.has_decider?
      decider = rule.decider
      original_subtarget = options.fetch(:original_subtarget)
      case decider.arity
      when 0
        if rule.decider_type == :if
          return decider.() ? BALI_TRUE : BALI_FALSE
        elsif rule.decider_type == :unless
          unless decider.()
            return BALI_TRUE
          else
            return BALI_FALSE
          end
        end
      when 1
        if rule.decider_type == :if
          return decider.(record) ? BALI_TRUE : BALI_FALSE
        elsif rule.decider_type == :unless
          unless decider.(record)
            return BALI_TRUE
          else
            return BALI_FALSE
          end
        end
      when 2
        if rule.decider_type == :if
          return decider.(record, original_subtarget) ? BALI_TRUE : BALI_FALSE
        elsif rule.decider_type == :unless
          unless decider.(record, original_subtarget)
            return BALI_TRUE
          else
            return BALI_FALSE
          end
        end
      end
    else
      return BALI_TRUE # rule is properly defined
    end # if rule has decider
  end # if rule is nil

  # return fuzy if otherly rule defines contrary to this cannot rule
  if other_rule_group.get_rule(:can, operation)
    return BALI_FUZY_TRUE
  else
    BALI_TRUE
  end
end

#bali_translate_response(bali_bool_value) ⇒ Object

translate response for value above to traditional true/false

Raises:



66
67
68
69
70
71
72
73
74
75
# File 'lib/bali/objector.rb', line 66

def bali_translate_response(bali_bool_value)
  raise Bali::Error, "Expect bali value to be an integer" unless bali_bool_value.is_a?(Integer)
  if bali_bool_value < 0
    return false
  elsif bali_bool_value > 0
    return true
  else 
    raise Bali::Error, "Bali bool value can either be negative or positive integer"
  end
end

#bali_translate_subtarget_roles(_subtarget_roles) ⇒ Object

will return array



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/bali/objector.rb', line 78

def bali_translate_subtarget_roles(_subtarget_roles)
  if _subtarget_roles.is_a?(String) || _subtarget_roles.is_a?(Symbol) || _subtarget_roles.is_a?(NilClass)
    return [_subtarget_roles]
  elsif _subtarget_roles.is_a?(Array)
    return _subtarget_roles
  else
    # this case, _subtarget_roles is an object but not a symbol or a string
    # let's try to deduct subtarget's roles

    _subtarget = _subtarget_roles
    _subtarget_class = _subtarget.class.to_s

    # variable to hold deducted role of the passed object
    deducted_roles = nil

    Bali::TRANSLATED_SUBTARGET_ROLES.each do |subtarget_class, roles_field_name|
      if _subtarget_class == subtarget_class
        deducted_roles = _subtarget.send(roles_field_name)
        if deducted_roles.is_a?(String) || deducted_roles.is_a?(Symbol) || deducted_roles.is_a?(NilClass)
          deducted_roles = [deducted_roles]
          break
        elsif deducted_roles.is_a?(Array)
          break
        end
      end # if matching class
    end # each TRANSLATED_SUBTARGET_ROLES

    if deducted_roles.nil?
      raise Bali::AuthorizationError, "Bali does not know how to process roles: #{_subtarget_roles}"
    end

    return deducted_roles
  end # if
end

#can!(subtarget_roles, operation, record = self, options = {}) ⇒ Object



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/bali/objector.rb', line 432

def can!(subtarget_roles, operation, record = self, options = {})
  can?(subtarget_roles, operation, record, options) do |original_subtarget, role, can_value|
    if !can_value
      auth_error = Bali::AuthorizationError.new
      auth_error.auth_level = :can
      auth_error.operation = operation
      auth_error.role = role
      auth_error.target = record
      auth_error.subtarget = original_subtarget

      if role
        auth_error.subtarget = original_subtarget if !(original_subtarget.is_a?(Symbol) || original_subtarget.is_a?(String) || original_subtarget.is_a?(Array))
      end

      raise auth_error
    end
  end
end

#can?(subtarget_roles, operation, record = self, options = {}) ⇒ Boolean

bali cannot

Returns:

  • (Boolean)


375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/bali/objector.rb', line 375

def can?(subtarget_roles, operation, record = self, options = {})
  subs = bali_translate_subtarget_roles(subtarget_roles)
  # well, it is largely not used unless decider's is 2 arity
  options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]

  can_value = BALI_FALSE 
  role = nil

  subs.each do |subtarget|
    next if can_value == BALI_TRUE
    role = subtarget
    can_value = bali_can?(role, operation, record, options)
  end

  if can_value == BALI_FALSE && block_given?
    yield options[:original_subtarget], role, bali_translate_response(can_value)
  end
  bali_translate_response can_value
rescue => e
  if e.is_a?(Bali::AuthorizationError)
    raise e
  else
    raise Bali::ObjectionError, e.message
  end
end

#cannot!(subtarget_roles, operation, record = self, options = {}) ⇒ Object



456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/bali/objector.rb', line 456

def cannot!(subtarget_roles, operation, record = self, options = {})
  cannot?(subtarget_roles, operation, record, options) do |original_subtarget, role, cant_value|
    if cant_value == false
      auth_error = Bali::AuthorizationError.new
      auth_error.auth_level = :cannot
      auth_error.operation = operation
      auth_error.role = role
      auth_error.target = record
      auth_error.subtarget = original_subtarget

      if role
        auth_error.subtarget = original_subtarget if !(original_subtarget.is_a?(Symbol) || original_subtarget.is_a?(String) || original_subtarget.is_a?(Array))
      end

      raise auth_error
    end
  end
end

#cannot?(subtarget_roles, operation, record = self, options = {}) ⇒ Boolean

Returns:

  • (Boolean)


406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/bali/objector.rb', line 406

def cannot?(subtarget_roles, operation, record = self, options = {})
  subs = bali_translate_subtarget_roles subtarget_roles
  options[:original_subtarget] = options[:original_subtarget].nil? ? subtarget_roles : options[:original_subtarget]

  cant_value = BALI_TRUE

  subs.each do |subtarget|
    next if cant_value == BALI_FALSE
    cant_value = bali_cannot?(subtarget, operation, record, options)
    if cant_value == BALI_FALSE
      role = subtarget
      if block_given?
        yield options[:original_subtarget], role, bali_translate_response(cant_value) 
      end
    end
  end

  bali_translate_response cant_value
rescue => e
  if e.is_a?(Bali::AuthorizationError)
    raise e
  else
    raise Bali::ObjectionError, e.message
  end
end

#cant!(subtarget_roles, operation, record = self, options = {}) ⇒ Object



451
452
453
454
# File 'lib/bali/objector.rb', line 451

def cant!(subtarget_roles, operation, record = self, options = {})
  puts "Deprecation Warning: please use cannot! instead, cant! will be deprecated on major release 3.0"
  cannot!(subtarget_roles, operation, record, options)
end

#cant?(subtarget_roles, operation, record = self, options = {}) ⇒ Boolean

Returns:

  • (Boolean)


401
402
403
404
# File 'lib/bali/objector.rb', line 401

def cant?(subtarget_roles, operation, record = self, options = {})
  puts "Deprecation Warning: please use cannot? instead, cant? will be deprecated on major release 3.0"
  cannot?(subtarget_roles, operation, record, options)
end