bali 2.4.0 → 6.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
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