3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
112
113
114
115
116
117
118
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
|
# File 'lib/isomorfeus_policy/lucid_policy/mixin.rb', line 3
def self.included(base)
base.instance_exec do
if RUBY_ENGINE != 'opal'
Isomorfeus.add_valid_policy_class(base) unless base == LucidPolicy::Base
end
def authorization_rules
@authorization_rules ||= { rules: {}.dup, policies: {}.dup, others: :deny }.dup
end
def all
:all
end
def allow(*classes_and_methods_and_options)
_allow_or_deny(:allow, *classes_and_methods_and_options)
end
def deny(*classes_and_methods_and_options)
_allow_or_deny(:deny, *classes_and_methods_and_options)
end
def others
:others
end
def rule(*classes_and_methods, &block)
_allow_or_deny(:rule, *classes_and_methods, &block)
end
def combine_with(policy_class, **options)
authorization_rules[:policies] = { policy_class => options }
end
def _allow_or_deny(thing, *classes_methods_options, &block)
rules = authorization_rules
if %i[allow deny].include?(thing) && %i[others all].include?(classes_methods_options.first)
rules[:others] = thing
return
end
target_classes = []
target_methods = []
target_options = {}
classes_methods_options.each do |class_method_option|
if class_method_option.class == Hash
target_options = class_method_option
else
class_or_method_s = class_method_option.to_s
if class_method_option.class == Symbol && class_or_method_s[0].downcase == class_or_method_s[0]
target_methods << class_method_option
else
class_method_option = class_or_method_s unless class_method_option.class == String
target_classes << class_method_option
end
end
end
thing_or_block = block_given? ? block : thing
target_classes.each do |target_class|
target_class = target_class.split('>::').last if target_class.start_with?('#<')
rules[:rules][target_class] = {} unless rules[:rules].key?(target_class)
if target_methods.empty?
rules[:rules][target_class][:rule] = thing_or_block
rules[:rules][target_class][:options] = target_options unless target_options.empty?
else
rules[:rules][target_class][:rule] = :deny unless rules[:rules][target_class].key?(:rule)
rules[:rules][target_class][:methods] = {} unless rules[:rules][target_class].key?(:methods)
target_methods.each do |target_method|
rules[:rules][target_class][:methods][target_method] = { rule: thing_or_block }
rules[:rules][target_class][:methods][target_method][:options] = target_options unless target_options.empty?
end
end
end
end
end
attr_reader :reason
def initialize(object, record_reason = nil)
@object = object
@reason = nil
@record_reason = record_reason
if @record_reason
@class_name = self.class.name
@class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
end
end
def authorized?(target_class, target_method = nil, props = nil)
Isomorfeus.raise_error(error_class: LucidPolicy::Exception, message: "#{self}: At least the class or class name must be given!") unless target_class
target_class = target_class.to_s unless target_class.class == String
target_class = target_class.split('>::').last if target_class.start_with?('#<')
rules = self.class.authorization_rules
result = if rules[:rules].key?(target_class)
if target_method && rules[:rules][target_class].key?(:methods) && rules[:rules][target_class][:methods].key?(target_method)
options = rules[:rules][target_class][:methods][target_method][:options]
rule = rules[:rules][target_class][:methods][target_method][:rule]
@reason = { policy_class: @class_name, class_name: target_class, method: target_method, rule: rule } if @record_reason
else
options = rules[:rules][target_class][:options]
rule = rules[:rules][target_class][:rule]
@reason = { policy_class: @class_name, class_name: target_class, rule: rule } if @record_reason
end
if rule.class == Symbol || rule.class == String
if options
condition, method_result = _get_condition_and_result(options)
if @record_reason
@reason[:condition] = condition
@reason[:condition_result] = method_result
end
rule if (condition == :if && method_result == true) || (condition == :if_not && method_result == false)
else
rule
end
else
props = LucidProps.new(props) unless props.class == LucidProps
policy_helper = LucidPolicy::Helper.new
policy_helper.instance_exec(@object, target_class, target_method, props, &rule)
r = policy_helper.result
@reason[:rule_result] = r if @record_reason
r
end
else
r = rules[:others]
@reason = { policy_class: @class_name, class_name: target_class, others: r } if @record_reason
r
end
return true if result == :allow
rules[:policies].each do |policy_class, options|
combined_policy_result = nil
if options.empty?
policy_instance = policy_class.new(@object, @record_reason)
combined_policy_result = policy_instance.authorized?(target_class, target_method, props)
@reason = @reason = { policy_class: @class_name, combined: policy_instance.reason } if @record_reason
else
condition, method_result = _get_condition_and_result(options)
if (condition == :if && method_result == true) || (condition == :if_not && method_result == false)
policy_instance = policy_class.new(@object, @record_reason)
combined_policy_result = policy_instance.authorized?(target_class, target_method, props)
@reason = { policy_class: @class_name, combined: policy_instance.reason, condition: condition, condition_result: method_result } if @record_reason
end
end
return true if combined_policy_result == true
end
result == :allow ? true : false
end
def authorized!(target_class, target_method = nil, props = nil)
result = authorized?(target_class, target_method, props)
reason_message = reason ? ", reason: #{reason}" : ''
return true if result
Isomorfeus.raise_error(error_class: LucidPolicy::Exception, message: "#{@object}: not authorized to call #{target_class}#{}#{target_method} #{props} #{reason_message}!")
end
def _get_condition_and_result(options)
condition = nil
method_name_or_block = if options.key?(:if)
condition = :if
options[:if]
elsif options.key?(:if_not)
condition = :if_not
options[:if_not]
elsif options.key?(:unless)
condition = :if_not
options[:unless]
end
method_result = if method_name_or_block && method_name_or_block.class == Symbol
@object.__send__(method_name_or_block)
else
props = LucidProps.new(props) unless props.class == LucidProps
method_name_or_block.call(@object, props)
end
[condition, method_result]
end
end
|