annotation_security 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/CHANGELOG.md +14 -0
  2. data/HOW-TO.md +275 -0
  3. data/{MIT-LICENSE → LICENSE} +1 -1
  4. data/README.md +39 -0
  5. data/Rakefile +62 -55
  6. data/assets/app/helpers/annotation_security_helper.rb +8 -8
  7. data/assets/config/initializers/annotation_security.rb +11 -11
  8. data/assets/config/security/relations.rb +20 -20
  9. data/assets/vendor/plugins/annotation_security/init.rb +13 -13
  10. data/bin/annotation_security +7 -7
  11. data/lib/annotation_security/exceptions.rb +124 -124
  12. data/lib/annotation_security/exec.rb +188 -188
  13. data/lib/annotation_security/filters.rb +37 -37
  14. data/lib/annotation_security/includes/action_controller.rb +144 -143
  15. data/lib/annotation_security/includes/active_record.rb +27 -27
  16. data/lib/annotation_security/includes/helper.rb +215 -215
  17. data/lib/annotation_security/includes/resource.rb +84 -84
  18. data/lib/annotation_security/includes/role.rb +30 -30
  19. data/lib/annotation_security/includes/user.rb +26 -26
  20. data/lib/annotation_security/manager/policy_factory.rb +29 -29
  21. data/lib/annotation_security/manager/policy_manager.rb +79 -79
  22. data/lib/annotation_security/manager/relation_loader.rb +272 -272
  23. data/lib/annotation_security/manager/resource_manager.rb +36 -36
  24. data/lib/annotation_security/manager/right_loader.rb +87 -87
  25. data/lib/annotation_security/model_observer.rb +61 -61
  26. data/lib/annotation_security/policy/abstract_policy.rb +344 -344
  27. data/lib/annotation_security/policy/abstract_static_policy.rb +75 -75
  28. data/lib/annotation_security/policy/all_resources_policy.rb +20 -20
  29. data/lib/annotation_security/policy/rule.rb +340 -340
  30. data/lib/annotation_security/policy/rule_set.rb +138 -138
  31. data/lib/annotation_security/rails.rb +38 -38
  32. data/lib/annotation_security/user_wrapper.rb +73 -73
  33. data/lib/annotation_security/utils.rb +141 -141
  34. data/lib/annotation_security/version.rb +10 -0
  35. data/lib/annotation_security.rb +102 -97
  36. data/lib/extensions/action_controller.rb +32 -32
  37. data/lib/extensions/active_record.rb +34 -34
  38. data/lib/extensions/filter.rb +133 -133
  39. data/lib/extensions/object.rb +10 -10
  40. data/lib/security_context.rb +589 -551
  41. data/spec/annotation_security/exceptions_spec.rb +16 -16
  42. data/spec/annotation_security/includes/helper_spec.rb +82 -82
  43. data/spec/annotation_security/manager/policy_manager_spec.rb +15 -15
  44. data/spec/annotation_security/manager/resource_manager_spec.rb +17 -17
  45. data/spec/annotation_security/manager/right_loader_spec.rb +17 -17
  46. data/spec/annotation_security/policy/abstract_policy_spec.rb +16 -16
  47. data/spec/annotation_security/policy/all_resources_policy_spec.rb +24 -24
  48. data/spec/annotation_security/policy/rule_set_spec.rb +112 -112
  49. data/spec/annotation_security/policy/rule_spec.rb +77 -77
  50. data/spec/annotation_security/policy/test_policy_spec.rb +80 -80
  51. data/spec/annotation_security/security_context_spec.rb +78 -78
  52. data/spec/annotation_security/utils_spec.rb +73 -73
  53. data/spec/helper/test_controller.rb +65 -65
  54. data/spec/helper/test_helper.rb +5 -5
  55. data/spec/helper/test_relations.rb +6 -6
  56. data/spec/helper/test_resource.rb +38 -38
  57. data/spec/helper/test_role.rb +21 -21
  58. data/spec/helper/test_user.rb +31 -31
  59. data/spec/rails_stub.rb +37 -37
  60. metadata +94 -72
  61. data/CHANGELOG +0 -2
  62. data/HOW-TO +0 -261
  63. data/README +0 -39
@@ -1,340 +1,340 @@
1
- #
2
- # = lib/annotation_security/policy/rule.rb
3
- #
4
-
5
- # = AnnotationSecurity::Rule
6
- # A right or a relation that belongs to a policy.
7
- #
8
- # Rules can be static or dynamic or both.
9
- # If the rule is a right, these values will be evaluated lazily.
10
- #
11
- class AnnotationSecurity::Rule # :nodoc:
12
-
13
- # Initialize a rule
14
- #
15
- def initialize(name,policy_class,*args,&block) # :nodoc:
16
- super()
17
- @name = name.to_sym
18
- @policy_class = policy_class
19
- @proc = block
20
- read_flags(args)
21
- read_options(args)
22
- if @proc
23
- initialize_for_proc(args)
24
- else
25
- initialize_for_string(args)
26
- end
27
- raise ArgumentError,
28
- "#{self}: Unexpected Arguments: #{args.join ','}" unless args.blank?
29
- #puts self
30
- end
31
-
32
- def to_s # :nodoc:
33
- "<#{full_name}[#{flag_s}]>"
34
- end
35
-
36
- def full_name # :nodoc:
37
- "#@policy_class##@name"
38
- end
39
-
40
- def flag_s # :nodoc:
41
- (@right ? 'r' : '-') +
42
- (@static.nil? ? '?' : (@static ? 's' : '-')) +
43
- (@dynamic.nil? ? '?' : (@dynamic ? 'd' : '-')) +
44
- (@req_user.nil? ? '?' : (@req_user ? 'u' : '-'))
45
- end
46
-
47
- # Return if this rule was defined as right
48
- #
49
- def right? # :nodoc:
50
- @right
51
- end
52
-
53
- # Return if this rule can be evaluated without a resource
54
- #
55
- def static? # :nodoc:
56
- return @static unless @static.nil?
57
- lazy_initialize
58
- @static
59
- end
60
-
61
- # Return if this rule can be evaluated with a resource
62
- #
63
- def dynamic? # :nodoc:
64
- return @dynamic unless @dynamic.nil?
65
- lazy_initialize
66
- @dynamic
67
- end
68
-
69
- def requires_credential? # :nodoc:
70
- return @req_user unless @req_user.nil?
71
- lazy_initialize
72
- @req_user
73
- end
74
-
75
- # Creates a method for a policy class that evaluates this rule
76
- # * +klass+ either @policy_class or its static partner
77
- #
78
- def extend_class(klass) # :nodoc:
79
-
80
- # Arguments passed to AbstractPolicy#user_roles
81
- # * +role+ symbol identifying the role a user must have (or nil)
82
- # * +user_required+ if false, the rule will also be
83
- # evaluated if the user is nil
84
- user_args = "#{@as ? ":#@as" : 'nil'},#{requires_credential?}"
85
-
86
- # Actual logic of the rule
87
- rule_code = @proc ? code_for_proc : code_for_string
88
-
89
- # Arguments passed to RuleExecutionError#new if an error occured
90
- # while evaluating the rule
91
- # * +rule+ full name of the rule
92
- # * +proc+ true iif this rule is defined with a proc
93
- # * +ex+ the original exeption
94
- ex_args = "'#{full_name}',#{@proc ? true : false},$!"
95
-
96
- code = "def #@name(*args) \n"
97
-
98
- # If parameter :is is given, @user.is_{@is}? has to return true.
99
- #
100
- code << "return false if @user.nil? || !@user.is_#@is?\n" if @is
101
- code << %{
102
- # __resource__ = @resource
103
- return user_roles(#{user_args}).any? do |__user__|
104
- #{rule_code}
105
- end
106
- rescue StandardError
107
- raise $! if $!.is_a? AnnotationSecurity::SecurityError
108
- raise AnnotationSecurity::RuleExecutionError.new(#{ex_args})
109
- end}
110
- klass.class_eval(code)
111
- self
112
- end
113
-
114
- # Evaluate proc for policy
115
- def evaluate(policy,*args) # :nodoc:
116
- raise AnnotationSecurity::RuleError, "#{self}: This rule has no proc" unless @proc
117
- if @arity == 0
118
- policy.instance_exec(&@proc)
119
- elsif @arity > 0
120
- policy.instance_exec(*(args[0..@arity-1]),&@proc)
121
- else
122
- policy.instance_exec(*args,&@proc)
123
- end
124
- end
125
-
126
- # Creates a copy for policy class
127
- #
128
- def copy(policy_class) # :nodoc:
129
- args = [name, policy_class,flag,options,@condition].compact
130
- self.class.new(*args,&@proc)
131
- end
132
-
133
- def name # :nodoc:
134
- @name
135
- end
136
-
137
- private
138
-
139
- def read_flags(args)
140
- @right = false
141
- @static = false
142
- @dynamic = true
143
- @req_user = true
144
- if args.delete :right
145
- @right = true
146
- @req_user = @static = @dynamic = nil
147
- elsif args.delete :system
148
- @static = true
149
- @dynamic = false
150
- elsif args.delete :pretest
151
- @static = true
152
- else
153
- args.delete :resource # default
154
- end
155
- end
156
-
157
- def flag
158
- return :right if right?
159
- if static?
160
- return :pretest if dynamic?
161
- return :system
162
- else
163
- return :resource
164
- end
165
- end
166
-
167
- def read_options(args)
168
- hash = args.detect {|h| h.is_a? Hash}
169
- args.delete hash
170
- return if hash.blank?
171
- @as = hash.delete(:as)
172
- @is = hash.delete(:is)
173
- @req_user = hash.delete(:require_credential)
174
- @req_user = true if @req_user.nil? && !right?
175
- if (@as || @is) && !@req_user
176
- raise ArgumentError, "Options :as and :is always require a user!"
177
- end
178
- unless hash.empty?
179
- raise ArgumentError, "Unexpected keys [#{hash.keys.join(', ')}]"
180
- end
181
- end
182
-
183
- def options
184
- {:is => @is, :as => @as, :require_credential => (right? ? nil : requires_credential?)}
185
- end
186
-
187
- # Check for the optional parameter :as => :role
188
- def initialize_for_proc(args)
189
- @arity = @proc.arity
190
- end
191
-
192
- def initialize_for_string(args)
193
- @condition = args.detect {|s| s.is_a? String } || 'true'
194
- args.delete @condition
195
- end
196
-
197
- # Find out if this rule can be evaluated statically
198
- def lazy_initialize
199
- raise_evil_recursion if @initialize_static
200
- @initialize_static = true
201
- if @proc
202
- # rules with proc must be defined as static explicitly
203
- @static = false
204
- @dynamic = true
205
- @req_user = true
206
- else
207
- # parse string to find out more
208
- if @condition =~ /:|self/
209
- # this only works with resources
210
- @static = false
211
- @dynamic = true
212
- @req_user = true
213
- else
214
- @static = true # a right is static if it uses only static rules
215
- @dynamic = false # a right is dynamic if it uses at least one dynamic rule
216
- @req_user = false # unless at least one rule requires a user
217
- @condition.gsub(/\(|\)/,' ').split.each do |token|
218
- unless token =~ /\A(if|unless|or|and|not|true|false|nil)\Z/
219
- token = validate_token!(token)
220
- @static &= can_be_static?(token)
221
- @dynamic |= can_be_dynamic?(token)
222
- @req_user |= needs_user?(token)
223
- end
224
- end
225
- end
226
- end
227
- raise AnnotationSecurity::RuleError,
228
- "#{self} is neither static nor dynamic!" unless @static || @dynamic
229
- end
230
-
231
- def validate_token!(token)
232
- return token.to_sym if @policy_class.has_rule?(token.to_sym)
233
- body = AnnotationSecurity::Utils.method_body(token)
234
- return validate_token!(body) if body
235
- raise AnnotationSecurity::RuleNotFoundError, "Unknown rule '#{token}' in #{full_name}"
236
- end
237
-
238
- def can_be_static?(token)
239
- @policy_class.has_static_rule?(token)
240
- end
241
-
242
- def can_be_dynamic?(token)
243
- @policy_class.has_dynamic_rule?(token)
244
- end
245
-
246
- def needs_user?(token)
247
- @policy_class.get_rule(token).requires_credential?
248
- end
249
-
250
- def raise_evil_recursion
251
- raise AnnotationSecurity::RuleError,
252
- "Forbidden recursion in #{@policy_class.resource_class}: #{self}"
253
- end
254
-
255
- def code_for_proc
256
- "evaluate_rule(:#{name},__user__,args)"
257
- end
258
-
259
- def code_for_string
260
- condition = @condition.dup
261
-
262
- # Apply special role 'self'
263
- condition.gsub!('self', '__self__')
264
-
265
- apply_resource_notation(condition)
266
- apply_may_property_notation(condition)
267
-
268
- if condition =~ /\A(\s*)(if|unless)/
269
- # multilines in case +condition+ contains comments
270
- "#{condition} \n true \n else \n false \n end"
271
- else
272
- condition
273
- end
274
- end
275
-
276
- # Apply replacements for :resource notation:
277
- # Rules of the form prefix(:resource.res_suffix, additional_args) are
278
- # rewritten to @resource.res_suffix.policy_for(@user).prefix(additional_args)
279
- #
280
- # They are per definition dynamic!
281
- # Other rules which contain :resource are per se dynamic, too!
282
- #
283
- def apply_resource_notation(condition)
284
- regex = /([^\s\(]+)\(:resource(?:\.([^\s,]*))?(?:,\s*([^\(]*))?\)/
285
-
286
- condition.gsub!(regex) do |match|
287
- parse_expr(match.scan(regex).first)
288
- end
289
- #condition.gsub!(/:resource/, "@resource")
290
- end
291
-
292
- def parse_expr(expr)
293
- prefix = expr.at(0)
294
- res_suffix = expr.at(1)
295
- additional_args = expr.at(2)
296
-
297
- case prefix
298
- when /^(if|unless|or|and|not)$/
299
- # Should not be matched by regex
300
- raise ArgumentError, "Invalid syntax."
301
- else
302
- res_class, right = parse_right(prefix)
303
- if res_class
304
- "(PolicyManager.get_policy("+
305
- ":#{res_class},@user,@resource.#{res_suffix}).#{right})"
306
- else
307
- call = "(!(res = @resource"
308
- call << ".#{res_suffix}" if res_suffix
309
- call << ").nil? &&"
310
- call << "res.policy_for(@user).#{prefix}"
311
- call << "(#{additional_args})" if additional_args
312
- call << ')'
313
- call
314
- end
315
- end
316
- end
317
-
318
- # Apply replacements for 'may: property' notation
319
- def apply_may_property_notation(condition)
320
- rx_may_prop = /(\S+):\s*(\S+)/
321
- condition.gsub!(rx_may_prop) do |match|
322
- right, resource = match.scan(rx_may_prop).first
323
- res_class, right = parse_right(right)
324
- if res_class
325
- "(PolicyManager.get_policy("+
326
- ":#{res_class},@user,@resource.#{resource}).#{right})"
327
- else
328
- "(!(res = @resource.#{resource}).nil? &&" +
329
- " res.policy_for(@user).#{right})"
330
- end
331
- end
332
- end
333
-
334
- # Returns [res_class, right] if +right+ has the form "res_class.right",
335
- # else it returns [nil, right].
336
- def parse_right(right)
337
- rx_class_right = /(\S*)\.(\S*)/
338
- (right.scan(rx_class_right).first) || [nil,right]
339
- end
340
- end
1
+ #
2
+ # = lib/annotation_security/policy/rule.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::Rule
6
+ # A right or a relation that belongs to a policy.
7
+ #
8
+ # Rules can be static or dynamic or both.
9
+ # If the rule is a right, these values will be evaluated lazily.
10
+ #
11
+ class AnnotationSecurity::Rule # :nodoc:
12
+
13
+ # Initialize a rule
14
+ #
15
+ def initialize(name,policy_class,*args,&block) # :nodoc:
16
+ super()
17
+ @name = name.to_sym
18
+ @policy_class = policy_class
19
+ @proc = block
20
+ read_flags(args)
21
+ read_options(args)
22
+ if @proc
23
+ initialize_for_proc(args)
24
+ else
25
+ initialize_for_string(args)
26
+ end
27
+ raise ArgumentError,
28
+ "#{self}: Unexpected Arguments: #{args.join ','}" unless args.blank?
29
+ #puts self
30
+ end
31
+
32
+ def to_s # :nodoc:
33
+ "<#{full_name}[#{flag_s}]>"
34
+ end
35
+
36
+ def full_name # :nodoc:
37
+ "#@policy_class##@name"
38
+ end
39
+
40
+ def flag_s # :nodoc:
41
+ (@right ? 'r' : '-') +
42
+ (@static.nil? ? '?' : (@static ? 's' : '-')) +
43
+ (@dynamic.nil? ? '?' : (@dynamic ? 'd' : '-')) +
44
+ (@req_user.nil? ? '?' : (@req_user ? 'u' : '-'))
45
+ end
46
+
47
+ # Return if this rule was defined as right
48
+ #
49
+ def right? # :nodoc:
50
+ @right
51
+ end
52
+
53
+ # Return if this rule can be evaluated without a resource
54
+ #
55
+ def static? # :nodoc:
56
+ return @static unless @static.nil?
57
+ lazy_initialize
58
+ @static
59
+ end
60
+
61
+ # Return if this rule can be evaluated with a resource
62
+ #
63
+ def dynamic? # :nodoc:
64
+ return @dynamic unless @dynamic.nil?
65
+ lazy_initialize
66
+ @dynamic
67
+ end
68
+
69
+ def requires_credential? # :nodoc:
70
+ return @req_user unless @req_user.nil?
71
+ lazy_initialize
72
+ @req_user
73
+ end
74
+
75
+ # Creates a method for a policy class that evaluates this rule
76
+ # * +klass+ either @policy_class or its static partner
77
+ #
78
+ def extend_class(klass) # :nodoc:
79
+
80
+ # Arguments passed to AbstractPolicy#user_roles
81
+ # * +role+ symbol identifying the role a user must have (or nil)
82
+ # * +user_required+ if false, the rule will also be
83
+ # evaluated if the user is nil
84
+ user_args = "#{@as ? ":#@as" : 'nil'},#{requires_credential?}"
85
+
86
+ # Actual logic of the rule
87
+ rule_code = @proc ? code_for_proc : code_for_string
88
+
89
+ # Arguments passed to RuleExecutionError#new if an error occured
90
+ # while evaluating the rule
91
+ # * +rule+ full name of the rule
92
+ # * +proc+ true iif this rule is defined with a proc
93
+ # * +ex+ the original exeption
94
+ ex_args = "'#{full_name}',#{@proc ? true : false},$!"
95
+
96
+ code = "def #@name(*args) \n"
97
+
98
+ # If parameter :is is given, @user.is_{@is}? has to return true.
99
+ #
100
+ code << "return false if @user.nil? || !@user.is_#@is?\n" if @is
101
+ code << %{
102
+ # __resource__ = @resource
103
+ return user_roles(#{user_args}).any? do |__user__|
104
+ #{rule_code}
105
+ end
106
+ rescue StandardError
107
+ raise $! if $!.is_a? AnnotationSecurity::SecurityError
108
+ raise AnnotationSecurity::RuleExecutionError.new(#{ex_args})
109
+ end}
110
+ klass.class_eval(code)
111
+ self
112
+ end
113
+
114
+ # Evaluate proc for policy
115
+ def evaluate(policy,*args) # :nodoc:
116
+ raise AnnotationSecurity::RuleError, "#{self}: This rule has no proc" unless @proc
117
+ if @arity == 0
118
+ policy.instance_exec(&@proc)
119
+ elsif @arity > 0
120
+ policy.instance_exec(*(args[0..@arity-1]),&@proc)
121
+ else
122
+ policy.instance_exec(*args,&@proc)
123
+ end
124
+ end
125
+
126
+ # Creates a copy for policy class
127
+ #
128
+ def copy(policy_class) # :nodoc:
129
+ args = [name, policy_class,flag,options,@condition].compact
130
+ self.class.new(*args,&@proc)
131
+ end
132
+
133
+ def name # :nodoc:
134
+ @name
135
+ end
136
+
137
+ private
138
+
139
+ def read_flags(args)
140
+ @right = false
141
+ @static = false
142
+ @dynamic = true
143
+ @req_user = true
144
+ if args.delete :right
145
+ @right = true
146
+ @req_user = @static = @dynamic = nil
147
+ elsif args.delete :system
148
+ @static = true
149
+ @dynamic = false
150
+ elsif args.delete :pretest
151
+ @static = true
152
+ else
153
+ args.delete :resource # default
154
+ end
155
+ end
156
+
157
+ def flag
158
+ return :right if right?
159
+ if static?
160
+ return :pretest if dynamic?
161
+ return :system
162
+ else
163
+ return :resource
164
+ end
165
+ end
166
+
167
+ def read_options(args)
168
+ hash = args.detect {|h| h.is_a? Hash}
169
+ args.delete hash
170
+ return if hash.blank?
171
+ @as = hash.delete(:as)
172
+ @is = hash.delete(:is)
173
+ @req_user = hash.delete(:require_credential)
174
+ @req_user = true if @req_user.nil? && !right?
175
+ if (@as || @is) && !@req_user
176
+ raise ArgumentError, "Options :as and :is always require a user!"
177
+ end
178
+ unless hash.empty?
179
+ raise ArgumentError, "Unexpected keys [#{hash.keys.join(', ')}]"
180
+ end
181
+ end
182
+
183
+ def options
184
+ {:is => @is, :as => @as, :require_credential => (right? ? nil : requires_credential?)}
185
+ end
186
+
187
+ # Check for the optional parameter :as => :role
188
+ def initialize_for_proc(args)
189
+ @arity = @proc.arity
190
+ end
191
+
192
+ def initialize_for_string(args)
193
+ @condition = args.detect {|s| s.is_a? String } || 'true'
194
+ args.delete @condition
195
+ end
196
+
197
+ # Find out if this rule can be evaluated statically
198
+ def lazy_initialize
199
+ raise_evil_recursion if @initialize_static
200
+ @initialize_static = true
201
+ if @proc
202
+ # rules with proc must be defined as static explicitly
203
+ @static = false
204
+ @dynamic = true
205
+ @req_user = true
206
+ else
207
+ # parse string to find out more
208
+ if @condition =~ /:|self/
209
+ # this only works with resources
210
+ @static = false
211
+ @dynamic = true
212
+ @req_user = true
213
+ else
214
+ @static = true # a right is static if it uses only static rules
215
+ @dynamic = false # a right is dynamic if it uses at least one dynamic rule
216
+ @req_user = false # unless at least one rule requires a user
217
+ @condition.gsub(/\(|\)/,' ').split.each do |token|
218
+ unless token =~ /\A(if|unless|or|and|not|true|false|nil)\Z/
219
+ token = validate_token!(token)
220
+ @static &= can_be_static?(token)
221
+ @dynamic |= can_be_dynamic?(token)
222
+ @req_user |= needs_user?(token)
223
+ end
224
+ end
225
+ end
226
+ end
227
+ raise AnnotationSecurity::RuleError,
228
+ "#{self} is neither static nor dynamic!" unless @static || @dynamic
229
+ end
230
+
231
+ def validate_token!(token)
232
+ return token.to_sym if @policy_class.has_rule?(token.to_sym)
233
+ body = AnnotationSecurity::Utils.method_body(token)
234
+ return validate_token!(body) if body
235
+ raise AnnotationSecurity::RuleNotFoundError, "Unknown rule '#{token}' in #{full_name}"
236
+ end
237
+
238
+ def can_be_static?(token)
239
+ @policy_class.has_static_rule?(token)
240
+ end
241
+
242
+ def can_be_dynamic?(token)
243
+ @policy_class.has_dynamic_rule?(token)
244
+ end
245
+
246
+ def needs_user?(token)
247
+ @policy_class.get_rule(token).requires_credential?
248
+ end
249
+
250
+ def raise_evil_recursion
251
+ raise AnnotationSecurity::RuleError,
252
+ "Forbidden recursion in #{@policy_class.resource_class}: #{self}"
253
+ end
254
+
255
+ def code_for_proc
256
+ "evaluate_rule(:#{name},__user__,args)"
257
+ end
258
+
259
+ def code_for_string
260
+ condition = @condition.dup
261
+
262
+ # Apply special role 'self'
263
+ condition.gsub!('self', '__self__')
264
+
265
+ apply_resource_notation(condition)
266
+ apply_may_property_notation(condition)
267
+
268
+ if condition =~ /\A(\s*)(if|unless)/
269
+ # multilines in case +condition+ contains comments
270
+ "#{condition} \n true \n else \n false \n end"
271
+ else
272
+ condition
273
+ end
274
+ end
275
+
276
+ # Apply replacements for :resource notation:
277
+ # Rules of the form prefix(:resource.res_suffix, additional_args) are
278
+ # rewritten to @resource.res_suffix.policy_for(@user).prefix(additional_args)
279
+ #
280
+ # They are per definition dynamic!
281
+ # Other rules which contain :resource are per se dynamic, too!
282
+ #
283
+ def apply_resource_notation(condition)
284
+ regex = /([^\s\(]+)\(:resource(?:\.([^\s,]*))?(?:,\s*([^\(]*))?\)/
285
+
286
+ condition.gsub!(regex) do |match|
287
+ parse_expr(match.scan(regex).first)
288
+ end
289
+ #condition.gsub!(/:resource/, "@resource")
290
+ end
291
+
292
+ def parse_expr(expr)
293
+ prefix = expr.at(0)
294
+ res_suffix = expr.at(1)
295
+ additional_args = expr.at(2)
296
+
297
+ case prefix
298
+ when /^(if|unless|or|and|not)$/
299
+ # Should not be matched by regex
300
+ raise ArgumentError, "Invalid syntax."
301
+ else
302
+ res_class, right = parse_right(prefix)
303
+ if res_class
304
+ "(PolicyManager.get_policy("+
305
+ ":#{res_class},@user,@resource.#{res_suffix}).#{right})"
306
+ else
307
+ call = "(!(res = @resource"
308
+ call << ".#{res_suffix}" if res_suffix
309
+ call << ").nil? &&"
310
+ call << "res.policy_for(@user).#{prefix}"
311
+ call << "(#{additional_args})" if additional_args
312
+ call << ')'
313
+ call
314
+ end
315
+ end
316
+ end
317
+
318
+ # Apply replacements for 'may: property' notation
319
+ def apply_may_property_notation(condition)
320
+ rx_may_prop = /(\S+):\s*(\S+)/
321
+ condition.gsub!(rx_may_prop) do |match|
322
+ right, resource = match.scan(rx_may_prop).first
323
+ res_class, right = parse_right(right)
324
+ if res_class
325
+ "(PolicyManager.get_policy("+
326
+ ":#{res_class},@user,@resource.#{resource}).#{right})"
327
+ else
328
+ "(!(res = @resource.#{resource}).nil? &&" +
329
+ " res.policy_for(@user).#{right})"
330
+ end
331
+ end
332
+ end
333
+
334
+ # Returns [res_class, right] if +right+ has the form "res_class.right",
335
+ # else it returns [nil, right].
336
+ def parse_right(right)
337
+ rx_class_right = /(\S*)\.(\S*)/
338
+ (right.scan(rx_class_right).first) || [nil,right]
339
+ end
340
+ end