bali 2.4.0 → 6.0.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 +5 -13
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +44 -2
- data/gemfiles/Gemfile-rails.5.0.x +7 -0
- data/gemfiles/Gemfile-rails.5.1.x +6 -0
- data/gemfiles/Gemfile-rails.5.2.x +6 -0
- data/gemfiles/Gemfile-rails.6.0.x +5 -0
- data/gemfiles/Gemfile-rails.edge +14 -0
- data/lib/bali.rb +33 -22
- data/lib/bali/config.rb +12 -0
- data/lib/bali/dsl_error.rb +3 -0
- data/lib/bali/{foundations/exceptions/bali_error.rb → error.rb} +0 -0
- data/lib/bali/judge.rb +221 -0
- data/lib/bali/printer.rb +42 -31
- data/lib/bali/rails/action_controller.rb +17 -0
- data/lib/bali/rails/action_view.rb +12 -0
- data/lib/bali/rails/active_record.rb +11 -0
- data/lib/bali/railtie.rb +13 -0
- data/lib/bali/role.rb +110 -0
- data/lib/bali/rspec/able_to_matcher.rb +39 -0
- data/lib/bali/rule.rb +17 -0
- data/lib/bali/ruler.rb +38 -0
- data/lib/bali/rules.rb +51 -0
- data/lib/bali/statics/active_record.rb +2 -0
- data/lib/bali/statics/authorizer.rb +65 -0
- data/lib/bali/statics/record.rb +13 -0
- data/lib/bali/statics/scope_ruler.rb +39 -0
- data/lib/bali/tasks/bali/print_rules.rake +9 -0
- data/lib/bali/version.rb +1 -1
- data/lib/generators/rails/USAGE +8 -0
- data/lib/generators/rails/rules_generator.rb +17 -0
- data/lib/generators/rails/templates/rules.rb +4 -0
- data/lib/generators/rspec/rules_generator.rb +12 -0
- data/lib/generators/rspec/templates/rules_spec.rb +7 -0
- metadata +131 -49
- data/lib/bali/dsl/map_rules_dsl.rb +0 -75
- data/lib/bali/dsl/rules_for_dsl.rb +0 -130
- data/lib/bali/foundations/all_foundations.rb +0 -17
- data/lib/bali/foundations/exceptions/authorization_error.rb +0 -38
- data/lib/bali/foundations/exceptions/dsl_error.rb +0 -3
- data/lib/bali/foundations/exceptions/objection_error.rb +0 -3
- data/lib/bali/foundations/judger/judge.rb +0 -329
- data/lib/bali/foundations/judger/negative_judge.rb +0 -40
- data/lib/bali/foundations/judger/positive_judge.rb +0 -41
- data/lib/bali/foundations/role_extractor.rb +0 -61
- data/lib/bali/foundations/rule/rule.rb +0 -55
- data/lib/bali/foundations/rule/rule_class.rb +0 -54
- data/lib/bali/foundations/rule/rule_group.rb +0 -91
- data/lib/bali/integrators/all_integrators.rb +0 -8
- data/lib/bali/integrators/rule_class_integrator.rb +0 -27
- data/lib/bali/integrators/rule_group_integrator.rb +0 -29
- data/lib/bali/integrators/rule_integrator.rb +0 -56
- data/lib/bali/objector.rb +0 -173
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'active_support/lazy_load_hooks'
|
5
|
+
|
6
|
+
ActiveSupport.on_load :action_controller do
|
7
|
+
require "bali"
|
8
|
+
::ActionController::Base.send :include, Bali::Statics::Authorizer
|
9
|
+
::ActionController::Base.send :include, Bali::Statics::ScopeRuler
|
10
|
+
|
11
|
+
if defined? ::ActionController::API
|
12
|
+
::ActionController::API.send :include, Bali::Statics::Authorizer
|
13
|
+
::ActionController::API.send :include, Bali::Statics::ScopeRuler
|
14
|
+
end
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'active_support/lazy_load_hooks'
|
5
|
+
|
6
|
+
ActiveSupport.on_load :action_view do
|
7
|
+
require "bali"
|
8
|
+
::ActionView::Base.send :include, Bali::Statics::Authorizer
|
9
|
+
::ActionView::Base.send :include, Bali::Statics::ScopeRuler
|
10
|
+
end
|
11
|
+
rescue LoadError
|
12
|
+
end
|
data/lib/bali/railtie.rb
ADDED
data/lib/bali/role.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
class Bali::Role
|
2
|
+
RIGHTS = [
|
3
|
+
INHERIT = :inherit,
|
4
|
+
DEFAULT_DENY = :default_deny,
|
5
|
+
DEFAULT_ALLOW = :default_allow
|
6
|
+
].freeze
|
7
|
+
|
8
|
+
IDENTIFIER_CLASSES = [
|
9
|
+
String,
|
10
|
+
Symbol,
|
11
|
+
NilClass,
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
attr_accessor :name
|
15
|
+
attr_accessor :cans, :cants
|
16
|
+
attr_reader :scope
|
17
|
+
|
18
|
+
attr_accessor :can_all
|
19
|
+
alias :can_all? :can_all
|
20
|
+
|
21
|
+
attr_accessor :right_level
|
22
|
+
|
23
|
+
def self.formalize(object)
|
24
|
+
case object
|
25
|
+
when *IDENTIFIER_CLASSES then [object]
|
26
|
+
when Array then (object.count == 0 ? nil : object)
|
27
|
+
else formalize(extract_roles_from_object(object))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.extract_roles_from_object(object)
|
32
|
+
method_name = object.class.role_field_for_authorization
|
33
|
+
|
34
|
+
method_name ?
|
35
|
+
formalize(object.send(method_name)) :
|
36
|
+
formalize(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(name)
|
40
|
+
@name = name.to_sym if name
|
41
|
+
@right_level = INHERIT
|
42
|
+
|
43
|
+
@cans = {}
|
44
|
+
@cants = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def can_all?
|
48
|
+
right_level == DEFAULT_ALLOW
|
49
|
+
end
|
50
|
+
|
51
|
+
def cant_all?
|
52
|
+
right_level == DEFAULT_DENY
|
53
|
+
end
|
54
|
+
|
55
|
+
##### DSL METHODS
|
56
|
+
def can(*args, &block)
|
57
|
+
add :can, *args, block
|
58
|
+
end
|
59
|
+
|
60
|
+
def cant(*args, &block)
|
61
|
+
add :cant, *args, block
|
62
|
+
end
|
63
|
+
|
64
|
+
def can_all
|
65
|
+
@right_level = DEFAULT_ALLOW
|
66
|
+
end
|
67
|
+
|
68
|
+
def cant_all
|
69
|
+
@right_level = DEFAULT_DENY
|
70
|
+
end
|
71
|
+
|
72
|
+
def scope(&block)
|
73
|
+
return @scope unless block_given?
|
74
|
+
|
75
|
+
raise Bali::DslError, "Block can't be scoped inside a role" if name
|
76
|
+
@scope = block
|
77
|
+
end
|
78
|
+
|
79
|
+
def add(term, *operations, block)
|
80
|
+
operations.each do |operation|
|
81
|
+
rule = Bali::Rule.new(term, operation)
|
82
|
+
rule.conditional = block if block
|
83
|
+
self << rule
|
84
|
+
end
|
85
|
+
end
|
86
|
+
##### DSL METHODS
|
87
|
+
|
88
|
+
def << rule
|
89
|
+
operation = rule.operation.to_sym
|
90
|
+
|
91
|
+
if rule.term == :cant
|
92
|
+
cants[operation] = rule
|
93
|
+
cans.delete operation
|
94
|
+
else
|
95
|
+
cans[operation] = rule
|
96
|
+
cants.delete operation
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_rule(term, operation)
|
101
|
+
case term
|
102
|
+
when :can then cans[operation.to_sym]
|
103
|
+
when :cant then cants[operation.to_sym]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def rules
|
108
|
+
cans.values + cants.values
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module BuiltIn
|
4
|
+
class AbleToMatcher < Be
|
5
|
+
def initialize(operation, class_or_record = nil)
|
6
|
+
@operation = operation
|
7
|
+
@class_or_record = class_or_record
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(actor)
|
11
|
+
if @class_or_record
|
12
|
+
rule_class = "#{@class_or_record.class.name}#{Bali.config.suffix}".constantize
|
13
|
+
rule_class.can?(actor, @operation, @class_or_record)
|
14
|
+
else
|
15
|
+
@class_or_record = actor
|
16
|
+
rule_class = "#{@class_or_record.name}#{Bali.config.suffix}".constantize
|
17
|
+
rule_class.can?(nil, @operation, @class_or_record)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure_message
|
22
|
+
"expected to be able to #{@operation}, but actually cannot"
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_message_when_negated
|
26
|
+
"expected not to be able to #{@operation}, but actually can"
|
27
|
+
end
|
28
|
+
|
29
|
+
def description
|
30
|
+
"be able to #{@operation}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def be_able_to(*args)
|
36
|
+
BuiltIn::AbleToMatcher.new(*args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/bali/rule.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This class represents a rule.
|
2
|
+
# can :delete
|
3
|
+
# A rule can also contains conditional part
|
4
|
+
class Bali::Rule
|
5
|
+
attr_reader :term
|
6
|
+
attr_reader :operation
|
7
|
+
attr_accessor :conditional
|
8
|
+
|
9
|
+
def initialize(term, operation)
|
10
|
+
@term = term
|
11
|
+
@operation = operation
|
12
|
+
end
|
13
|
+
|
14
|
+
def conditional?
|
15
|
+
@is_conditional ||= !!conditional
|
16
|
+
end
|
17
|
+
end
|
data/lib/bali/ruler.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# This class represents all roles, and its rules, for a resource
|
2
|
+
class Bali::Ruler
|
3
|
+
attr_reader :model_class
|
4
|
+
attr_accessor :roles
|
5
|
+
|
6
|
+
private :model_class
|
7
|
+
|
8
|
+
def self.for(record_class)
|
9
|
+
rule_class = Bali::Rules.for(record_class)
|
10
|
+
rule_class.ruler if rule_class
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(model_class)
|
14
|
+
@model_class = model_class
|
15
|
+
@roles = {}
|
16
|
+
@roles[nil] = Bali::Role.new(nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
def << role
|
20
|
+
@roles[role.name] = role
|
21
|
+
end
|
22
|
+
|
23
|
+
def [] role
|
24
|
+
symbolized_role = role.to_sym if role
|
25
|
+
@roles[symbolized_role]
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_or_create_role role_name
|
29
|
+
role = self[role_name]
|
30
|
+
|
31
|
+
if role.nil?
|
32
|
+
role = Bali::Role.new(role_name)
|
33
|
+
self << role
|
34
|
+
end
|
35
|
+
|
36
|
+
role
|
37
|
+
end
|
38
|
+
end
|
data/lib/bali/rules.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
class Bali::Rules
|
4
|
+
extend Bali::Statics::Authorizer
|
5
|
+
extend Bali::Statics::ScopeRuler
|
6
|
+
|
7
|
+
class << self
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
attr_writer :current_role
|
11
|
+
attr_reader :ruler
|
12
|
+
|
13
|
+
def_delegators :inheritable_role, :scope, :scope
|
14
|
+
def_delegators :inheritable_role, :can, :can
|
15
|
+
def_delegators :inheritable_role, :cant, :cant
|
16
|
+
def_delegators :inheritable_role, :cant_all, :cant_all
|
17
|
+
def_delegators :inheritable_role, :can_all, :can_all
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.for(record_class)
|
21
|
+
rule_maker_cls_str = "#{record_class}#{Bali.config.suffix}"
|
22
|
+
rule_maker_cls_str.safe_constantize
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.model_class
|
26
|
+
class_name = to_s
|
27
|
+
suffix = Bali.config.suffix
|
28
|
+
rule_class_maker_str = class_name[0...class_name.length - suffix.length]
|
29
|
+
rule_class_maker_str.constantize
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.role(*role_names, &block)
|
33
|
+
role_names.each do |role_name|
|
34
|
+
if Bali::Role::IDENTIFIER_CLASSES.include?(role_name.class)
|
35
|
+
role = ruler.find_or_create_role role_name
|
36
|
+
role.instance_eval(&block)
|
37
|
+
else
|
38
|
+
raise Bali::DslError, "Cannot define role using #{param.class}. " +
|
39
|
+
"Please use either a Symbol, a String or nil"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.ruler
|
45
|
+
@ruler ||= Bali::Ruler.new(model_class)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.inheritable_role
|
49
|
+
ruler[nil]
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Bali::Statics::Authorizer
|
2
|
+
module HelperFunctions
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def not_true_actor?(actor)
|
6
|
+
Symbol === actor || String === actor
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_actor(actor, operation, record = nil)
|
10
|
+
return actor unless not_true_actor?(actor)
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_operation(actor, operation, record = nil)
|
14
|
+
not_true_actor?(actor) ?
|
15
|
+
actor :
|
16
|
+
operation
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_record(actor, operation, record = nil)
|
20
|
+
if not_true_actor?(actor) && record.nil?
|
21
|
+
operation
|
22
|
+
elsif actor.is_a?(ActiveRecord::Base) && record.nil?
|
23
|
+
actor.class
|
24
|
+
else
|
25
|
+
record
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def determine_model_class!(obj, arg1, arg2, arg3)
|
30
|
+
if arg2.nil? && arg3.nil? && !obj.respond_to?(:model_class)
|
31
|
+
raise Bali::Error, "Cannot perform checking when the actor is not known"
|
32
|
+
end
|
33
|
+
arg3 = obj.model_class if (arg2.nil? || arg1.nil?) && arg3.nil?
|
34
|
+
arg3
|
35
|
+
end
|
36
|
+
|
37
|
+
def check(term, obj, arg1, arg2, arg3)
|
38
|
+
# try to infer current user if only passing one arg
|
39
|
+
if arg2.nil? && arg3.nil? && obj.respond_to?(:current_user)
|
40
|
+
arg2 = arg1
|
41
|
+
arg1 = obj.current_user
|
42
|
+
elsif arg3.nil? && obj.respond_to?(:current_user)
|
43
|
+
arg3 = arg2
|
44
|
+
arg2 = arg1
|
45
|
+
arg1 = obj.current_user
|
46
|
+
end
|
47
|
+
|
48
|
+
arg3 = HelperFunctions.determine_model_class! obj, arg1, arg2, arg3
|
49
|
+
|
50
|
+
actor = HelperFunctions.find_actor(arg1, arg2, arg3)
|
51
|
+
operation = HelperFunctions.find_operation(arg1, arg2, arg3)
|
52
|
+
record = HelperFunctions.find_record(arg1, arg2, arg3)
|
53
|
+
|
54
|
+
Bali::Judge.check(term, actor, operation, record)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def can?(arg1, arg2 = nil, arg3 = nil)
|
59
|
+
HelperFunctions.check(:can, self, arg1, arg2, arg3)
|
60
|
+
end
|
61
|
+
|
62
|
+
def cant?(arg1, arg2 = nil, arg3 = nil)
|
63
|
+
HelperFunctions.check(:cant, self, arg1, arg2, arg3)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Bali::Statics::Record
|
2
|
+
def self.extended(cls)
|
3
|
+
cls.class_eval do
|
4
|
+
class << self
|
5
|
+
attr_accessor :role_field_for_authorization
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.extract_roles_from method_name
|
9
|
+
@role_field_for_authorization = method_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Bali::Statics::ScopeRuler
|
2
|
+
module HelperFunctions
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def extract_data_and_actor(obj, arg1, arg2 = nil)
|
6
|
+
if arg2.nil?
|
7
|
+
data = arg1
|
8
|
+
if obj.respond_to?(:current_user)
|
9
|
+
actor = obj.current_user
|
10
|
+
end
|
11
|
+
else
|
12
|
+
data, actor = arg1, arg2
|
13
|
+
end
|
14
|
+
|
15
|
+
return data, actor
|
16
|
+
end
|
17
|
+
|
18
|
+
def scope_for(relation)
|
19
|
+
rule_class = Bali::Rules.for(relation.model)
|
20
|
+
return unless rule_class
|
21
|
+
|
22
|
+
rule_class.inheritable_role.scope
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def rule_scope(arg1, arg2 = nil)
|
27
|
+
data, actor = HelperFunctions.extract_data_and_actor(self, arg1, arg2)
|
28
|
+
return unless data
|
29
|
+
|
30
|
+
scope = HelperFunctions.scope_for(data)
|
31
|
+
scoped_data = case scope.arity
|
32
|
+
when 0 then scope.call
|
33
|
+
when 1 then scope.call(data)
|
34
|
+
when 2 then scope.call(data, actor)
|
35
|
+
end
|
36
|
+
|
37
|
+
scoped_data || data
|
38
|
+
end
|
39
|
+
end
|