bali 2.4.0 → 6.0.0rc1

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.
Files changed (41) hide show
  1. checksums.yaml +5 -13
  2. data/.rspec +1 -0
  3. data/lib/bali.rb +23 -14
  4. data/lib/bali/activerecord.rb +8 -0
  5. data/lib/bali/authorizer.rb +24 -0
  6. data/lib/bali/config.rb +12 -0
  7. data/lib/bali/dsl_error.rb +3 -0
  8. data/lib/bali/{foundations/exceptions/bali_error.rb → error.rb} +0 -0
  9. data/lib/bali/judge.rb +239 -0
  10. data/lib/bali/printer.rb +16 -26
  11. data/lib/bali/railtie.rb +13 -0
  12. data/lib/bali/role.rb +79 -0
  13. data/lib/bali/rule.rb +17 -0
  14. data/lib/bali/ruler.rb +36 -0
  15. data/lib/bali/rules.rb +68 -0
  16. data/lib/bali/tasks/bali/print_rules.rake +9 -0
  17. data/lib/bali/version.rb +1 -1
  18. data/lib/generators/rails/USAGE +8 -0
  19. data/lib/generators/rails/rules_generator.rb +17 -0
  20. data/lib/generators/rails/templates/rules.rb +4 -0
  21. data/lib/generators/rspec/rules_generator.rb +12 -0
  22. data/lib/generators/rspec/templates/rules_spec.rb +7 -0
  23. metadata +104 -47
  24. data/lib/bali/dsl/map_rules_dsl.rb +0 -75
  25. data/lib/bali/dsl/rules_for_dsl.rb +0 -130
  26. data/lib/bali/foundations/all_foundations.rb +0 -17
  27. data/lib/bali/foundations/exceptions/authorization_error.rb +0 -38
  28. data/lib/bali/foundations/exceptions/dsl_error.rb +0 -3
  29. data/lib/bali/foundations/exceptions/objection_error.rb +0 -3
  30. data/lib/bali/foundations/judger/judge.rb +0 -329
  31. data/lib/bali/foundations/judger/negative_judge.rb +0 -40
  32. data/lib/bali/foundations/judger/positive_judge.rb +0 -41
  33. data/lib/bali/foundations/role_extractor.rb +0 -61
  34. data/lib/bali/foundations/rule/rule.rb +0 -55
  35. data/lib/bali/foundations/rule/rule_class.rb +0 -54
  36. data/lib/bali/foundations/rule/rule_group.rb +0 -91
  37. data/lib/bali/integrators/all_integrators.rb +0 -8
  38. data/lib/bali/integrators/rule_class_integrator.rb +0 -27
  39. data/lib/bali/integrators/rule_group_integrator.rb +0 -29
  40. data/lib/bali/integrators/rule_integrator.rb +0 -56
  41. data/lib/bali/objector.rb +0 -173
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- M2I3NWNjNmVlNWM4YzQyMGU0YzAyNzBmZGJmNTBjMTUxMGYxOWMxYg==
5
- data.tar.gz: !binary |-
6
- MzhkMjlhYzA4NDU3M2M4ODE1ODkxZTU0ZDA3NDJjZGUwODNmOTZhYg==
2
+ SHA256:
3
+ metadata.gz: 6d59bbcd74624299c6680e6b33787924db86c22e95ea1eac9817835c90c8fd1b
4
+ data.tar.gz: 656f6455652098bd35ed58ea60434ace5643f2c5d6063a4ccf1e2b235156a79b
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MTJhOTRkODJhMWM5OTIzNjA0NTQ0OWVjMTMzOTgwZDFlZDBhZjQzNjQ0N2Y2
10
- YWViMDExYzMyZjI5Y2YyOTYxNDdhZWJmYWU5YTVlNDYwODgwZDA1NDhhZWRm
11
- NTliMTI1ZGEyMjNjY2QyNGEwNTM5YjNjNDc5OGIxYjk2MTYyNTI=
12
- data.tar.gz: !binary |-
13
- NTMzMzBjNTczZmM2MDE1YmI0NjA0ZjY1MDRhZGZmNWRiNTRjZGYzMDRmMWI4
14
- Zjk4ZTEzNGZkZDg0NzFmZjg0YjYwNDQ2OGVlYjgwNzkwNTY4MGFlZDE1NjI3
15
- ZDI1ZjlhNmFhODA4OGQzZDFhMjBkNjdjMjk1ZTI0YTkzZmQ4MTY=
6
+ metadata.gz: e66ac8fffcacebda88e299bef9e0ef512d0dbed4f9e4b309df3bec6b3afabe4af4fd2182f501df0847c396f918adcc128c68846fd163a049b56b2ec2c807884b
7
+ data.tar.gz: a629ff732df6cb1bc347de1bab16dafb87ad29d87029c3a87b30a4b0301da430cf15e47f5b6844f104a071d0f755dce18a0d29277a07c0f4bd5bdde12e7fd5e9
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require spec_helper
@@ -1,14 +1,17 @@
1
1
  require_relative "bali/version"
2
2
 
3
- # load the DSL syntax maker definition, ordered by proper order of invocation
4
- require_relative "bali/dsl/map_rules_dsl"
5
- require_relative "bali/dsl/rules_for_dsl"
6
-
7
- require_relative "bali/objector"
8
- require_relative "bali/printer"
3
+ begin
4
+ require "rails"
5
+ require "rails/generators"
6
+ rescue LoadError => e
7
+ # ignores
8
+ end
9
9
 
10
- require_relative "bali/foundations/all_foundations"
11
- require_relative "bali/integrators/all_integrators"
10
+ require "zeitwerk"
11
+ loader = Zeitwerk::Loader.for_gem
12
+ loader.ignore("#{__dir__}/generators")
13
+ loader.ignore("#{__dir__}/bali/activerecord.rb")
14
+ loader.setup
12
15
 
13
16
  module Bali
14
17
  # mapping class to a RuleClass
@@ -21,13 +24,19 @@ module Bali
21
24
  TRANSLATED_SUBTARGET_ROLES = {}
22
25
 
23
26
  extend self
24
- def map_rules(&block)
25
- dsl_map_rules = Bali::MapRulesDsl.new
26
- dsl_map_rules.instance_eval(&block)
27
+
28
+ def config
29
+ @config ||= Bali::Config.new
27
30
  end
28
31
 
29
- def clear_rules
30
- Bali::RULE_CLASS_MAP.clear
31
- true
32
+ def configure
33
+ yield config
34
+ end
35
+
36
+ if defined? Rails
37
+ require "bali/railtie"
38
+ require "bali/activerecord"
32
39
  end
33
40
  end
41
+
42
+ loader.eager_load
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/lazy_load_hooks'
4
+
5
+ ActiveSupport.on_load :active_record do
6
+ require "bali"
7
+ ::ActiveRecord::Base.send :include, Bali::Authorizer
8
+ end
@@ -0,0 +1,24 @@
1
+ module Bali::Authorizer
2
+ def self.included(base)
3
+ base.extend Bali::Authorizer::Statics
4
+ end
5
+
6
+ def can?(actor_or_roles, operation = nil)
7
+ self.class.can?(actor_or_roles, operation, self)
8
+ end
9
+
10
+ def cant?(actor_or_roles, operation = nil)
11
+ self.class.cant?(actor_or_roles, operation, self)
12
+ end
13
+ end
14
+
15
+ # to allow class-level objection
16
+ module Bali::Authorizer::Statics
17
+ def can?(actor_or_roles, operation, record = self)
18
+ Bali::Judge.check(:can, actor_or_roles, operation, record)
19
+ end
20
+
21
+ def cant?(actor_or_roles, operation, record = self)
22
+ Bali::Judge.check(:cant, actor_or_roles, operation, record)
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ class Bali::Config
2
+ attr_accessor :rules_path
3
+ attr_accessor :suffix
4
+
5
+ def initialize
6
+ if Rails.respond_to?(:root) && Rails.root
7
+ @rules_path = Rails.root.join("app", "rules")
8
+ end
9
+
10
+ @suffix = "Rules"
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ # Error class to represent incorrect usage of syntax
2
+ class Bali::DslError < Bali::Error
3
+ end
@@ -0,0 +1,239 @@
1
+ class Bali::Judge
2
+ # Fuzy value is possible when the evaluation is not yet clear cut, for example in this case:
3
+ #
4
+ # role :finance do
5
+ # cant :view
6
+ # end
7
+ #
8
+ # others do
9
+ # can :view
10
+ # can :index
11
+ # end
12
+ #
13
+ # Checking cant view for finance role results in a definite false, but
14
+ # checking on index for the same role results in FUZY_TRUE. Eventually, all FUZY value will be
15
+ # normal TRUE or FALSE if no definite counterpart is found.
16
+
17
+ FUZY_FALSE = -2
18
+ FUZY_TRUE = 2
19
+ DEFINITE_FALSE = -1
20
+ DEFINITE_TRUE = 1
21
+
22
+ attr_accessor :term,
23
+ :actor,
24
+ :role,
25
+ :operation,
26
+ :record,
27
+ :should_cross_check
28
+
29
+ class << self
30
+ def check(term, actor_or_roles, operation, record)
31
+ if operation.nil?
32
+ # eg: user.can? :sign_in
33
+ operation = actor_or_roles
34
+ actor_or_roles = nil
35
+ end
36
+
37
+ judgement_value = default_value = term == :can ? false : true
38
+ roles = Bali::Role.formalize actor_or_roles
39
+
40
+ roles.each do |role|
41
+ judge = Bali::Judge.new(
42
+ term: term,
43
+ role: role,
44
+ actor: actor_or_roles,
45
+ operation: operation,
46
+ record: record
47
+ )
48
+
49
+ judgement_value = judge.judgement
50
+ break if judgement_value != default_value
51
+ end
52
+
53
+ judgement_value
54
+ end
55
+ end
56
+
57
+ def initialize(term:,
58
+ actor:,
59
+ role:,
60
+ operation:,
61
+ record:,
62
+ should_cross_check: true)
63
+
64
+ @term = term
65
+ @role = role
66
+ @actor = actor
67
+ @operation = operation
68
+ @record = record
69
+ @should_cross_check = should_cross_check
70
+ end
71
+
72
+ def judgement
73
+ our_holy_judgement = natural_value if no_rule_group?
74
+
75
+ if our_holy_judgement.nil? && rule.nil? && may_have_reservation?
76
+ our_holy_judgement = cross_check_reverse_value(cross_check_judge.judgement)
77
+ end
78
+
79
+ if our_holy_judgement.nil? && rule.nil?
80
+ cross_check_value = nil
81
+ # default if can? for undefined rule is false, after related clause
82
+ # cant be found in cant?
83
+ if should_cross_check
84
+ cross_check_value = cross_check_judge.judgement
85
+ end
86
+
87
+ # if cross check value nil, then the reverse rule is not defined,
88
+ # let's determine whether they can do anything or not
89
+ if cross_check_value.nil?
90
+ # rule_group can be nil for when user checking under undefined rule-group
91
+ if rule_group
92
+ if rule_group.can_all?
93
+ our_holy_judgement = term == :cant ? DEFINITE_FALSE : DEFINITE_TRUE
94
+ elsif rule_group.cant_all?
95
+ our_holy_judgement = term == :cant ? DEFINITE_TRUE : DEFINITE_FALSE
96
+ end
97
+
98
+ end # if rule_group exist
99
+ else
100
+ # process value from cross checking
101
+
102
+ if otherly_rule && (cross_check_value == FUZY_FALSE || cross_check_value == FUZY_TRUE)
103
+ # give chance to check at others block
104
+ @rule = otherly_rule
105
+ else
106
+ our_holy_judgement = cross_check_reverse_value(cross_check_value)
107
+ end
108
+ end
109
+ end # if our judgement nil and rule is nil
110
+
111
+ # if our holy judgement is still nil, but rule is defined
112
+ if our_holy_judgement.nil? && rule
113
+ if rule.conditional?
114
+ our_holy_judgement = run_condition(rule, actor, record)
115
+ else
116
+ our_holy_judgement = DEFINITE_TRUE
117
+ end
118
+ end
119
+
120
+ # return fuzy if otherly rule defines contrary to this term
121
+ if our_holy_judgement.nil? && rule.nil? && (other_rule_group && other_rule_group.find_rule(reversed_term, operation))
122
+ if rule_group && (rule_group.can_all? || rule_group.cant_all?)
123
+ # don't overwrite our holy judgement with fuzy value if rule group
124
+ # zeus/plant, because zeus/plant is more definite than any fuzy values
125
+ # eventhough the rule is abstractly defined
126
+ else
127
+ our_holy_judgement = term == :cant ? FUZY_TRUE : FUZY_FALSE
128
+ end
129
+ end
130
+
131
+ # if at this point still nil, well,
132
+ # return the natural value for this judge
133
+ if our_holy_judgement.nil?
134
+ if otherly_rule
135
+ our_holy_judgement = FUZY_TRUE
136
+ else
137
+ our_holy_judgement = natural_value
138
+ end
139
+ end
140
+
141
+ return !should_cross_check ?
142
+ our_holy_judgement :
143
+
144
+ # translate response for value above to traditional true/false
145
+ # holy judgement refer to non-standard true/false being used inside Bali
146
+ # which need to be translated from other beings to know
147
+ our_holy_judgement > 0
148
+ end
149
+
150
+ private
151
+
152
+ def record_class
153
+ record.is_a?(Class) ? record : record.class
154
+ end
155
+
156
+ def ruler
157
+ @ruler ||= Bali::Ruler.for record_class
158
+ end
159
+
160
+ def rule_group_for(role)
161
+ ruler.nil? ? nil : ruler[role]
162
+ end
163
+
164
+ def rule_group
165
+ @rule_group ||= rule_group_for role
166
+ end
167
+
168
+ def other_rule_group
169
+ @other_rule_group ||= rule_group_for nil
170
+ end
171
+
172
+ def no_rule_group?
173
+ rule_group.nil? && other_rule_group.nil?
174
+ end
175
+
176
+ def rule
177
+ # rule group may be nil, for when user checking for undefined rule group
178
+ @rule ||= rule_group ? rule_group.find_rule(term, operation) : nil
179
+ end
180
+
181
+ def otherly_rule
182
+ @otherly_rule ||= other_rule_group ? other_rule_group.find_rule(term, operation) : nil
183
+ end
184
+
185
+ def cross_check_judge
186
+ @cross_check_judge ||= begin
187
+ Bali::Judge.new(
188
+ term: reversed_term,
189
+ role: role,
190
+ operation: operation,
191
+ record: record,
192
+ should_cross_check: false,
193
+ actor: actor
194
+ )
195
+ end
196
+ end
197
+
198
+ def reversed_term
199
+ case term
200
+ when :can then :cant
201
+ when :cant then :can
202
+ end
203
+ end
204
+
205
+ def natural_value
206
+ term == :cant ? DEFINITE_TRUE : DEFINITE_FALSE
207
+ end
208
+
209
+ # returns true if we need to check rule that can overwrite
210
+ # the most powerful rule defined
211
+ def may_have_reservation?
212
+ term == :cant ?
213
+ (rule_group && rule_group.cant_all?) :
214
+ (rule_group && rule_group.can_all?)
215
+ end
216
+
217
+ def run_condition(rule, actor, record)
218
+ # must test first
219
+ conditional = rule.conditional
220
+ case conditional.arity
221
+ when 0
222
+ return conditional.() ? DEFINITE_TRUE : DEFINITE_FALSE
223
+ when 1
224
+ return conditional.(record) ? DEFINITE_TRUE : DEFINITE_FALSE
225
+ when 2
226
+ return conditional.(record, actor) ? DEFINITE_TRUE : DEFINITE_FALSE
227
+ end
228
+ end
229
+
230
+ def cross_check_reverse_value(cross_check_value)
231
+ case cross_check_value
232
+ when DEFINITE_TRUE then DEFINITE_FALSE
233
+ when DEFINITE_FALSE then DEFINITE_TRUE
234
+ when FUZY_FALSE then FUZY_TRUE
235
+ when FUZY_TRUE then FUZY_FALSE
236
+ end
237
+ end
238
+
239
+ end
@@ -4,7 +4,7 @@ require "date"
4
4
  # module that would allow all defined rules to be printed for check
5
5
  module Bali::Printer
6
6
  module_function
7
-
7
+
8
8
  SEPARATOR = " " * 6
9
9
  SUBTARGET_TITLE_SEPARATOR = SEPARATOR + ("-" * 80) + "\n"
10
10
 
@@ -14,50 +14,40 @@ module Bali::Printer
14
14
  # build up the string for pretty printing rules
15
15
  Bali::RULE_CLASS_MAP.each do |klass, rule_class|
16
16
  output << "===== #{klass.to_s} =====\n\n"
17
-
18
- rule_class.rule_groups.each do |subtarget, rule_group|
19
- print_rule_group(rule_group, output)
20
- end
21
17
 
22
- if rule_class.others_rule_group.rules.any?
23
- print_rule_group(rule_class.others_rule_group, output)
18
+ rule_class.roles.each do |subtarget, role|
19
+ print_role role, output
24
20
  end
21
+
25
22
  output << "\n\n"
26
23
  end
27
24
 
28
- output << "\n\n"
29
- output << DateTime.now.strftime("Printed at %d-%m-%Y %I:%M%p %Z")
25
+ output << DateTime.now.strftime("Printed at %Y-%m-%d %I:%M%p %Z")
30
26
 
31
27
  output.string
32
28
  end
33
29
 
34
- def print_rule_group(rule_group, target_io)
35
- target = rule_group.target.to_s
36
- subtarget = rule_group.subtarget.to_s.capitalize
37
- subtarget = "Others" if subtarget == "__*__"
38
- is_zeus = rule_group.zeus?
39
- is_plant = rule_group.plant?
30
+ def print_role role, target_io
31
+ subtarget = role.subtarget.to_s.capitalize
32
+ subtarget = "By default" if subtarget.blank?
33
+ can_all = role.can_all?
40
34
  counter = 0
41
35
 
42
- target_io << "#{SEPARATOR}#{subtarget}, can all: #{is_zeus}, cannot all: #{is_plant}\n"
36
+ target_io << "#{SEPARATOR}#{subtarget}\n"
43
37
  target_io << SUBTARGET_TITLE_SEPARATOR
44
-
45
- if is_zeus
38
+
39
+ if can_all
46
40
  target_io << "#{SEPARATOR} #{counter+=1}. #{subtarget} can do anything except if explicitly stated otherwise\n"
47
- elsif is_plant
48
- target_io << "#{SEPARATOR} #{counter+=1}. #{subtarget} cannot do anything except if explicitly stated otherwise\n"
49
41
  end
50
-
51
- rule_group.rules.each do |rule|
42
+
43
+ role.rules.each do |rule|
52
44
  written_rule = StringIO.new
53
- written_rule << "#{SEPARATOR} #{counter+=1}. #{subtarget} #{rule.auth_val} #{rule.operation} #{target}"
54
- if rule.has_decider?
45
+ written_rule << "#{SEPARATOR} #{counter+=1}. #{subtarget} #{rule.term} #{rule.operation}"
46
+ if rule.conditional?
55
47
  written_rule << ", with condition"
56
48
  end
57
49
  written_rule << "\n"
58
50
  target_io << written_rule.string
59
51
  end
60
-
61
- target_io << "\n"
62
52
  end
63
53
  end
@@ -0,0 +1,13 @@
1
+ require "bali"
2
+ require "rails"
3
+
4
+ module Bali
5
+ class Railtie < ::Rails::Railtie
6
+ railtie_name :bali
7
+
8
+ rake_tasks do
9
+ path = File.expand_path(__dir__)
10
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
11
+ end
12
+ end
13
+ end