annotation_security 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/CHANGELOG +2 -0
  2. data/HOW-TO +261 -0
  3. data/MIT-LICENSE +18 -0
  4. data/README +39 -0
  5. data/Rakefile +56 -0
  6. data/assets/app/helpers/annotation_security_helper.rb +9 -0
  7. data/assets/config/initializers/annotation_security.rb +12 -0
  8. data/assets/config/security/relations.rb +20 -0
  9. data/assets/config/security/rights.yml +16 -0
  10. data/assets/vendor/plugins/annotation_security/init.rb +14 -0
  11. data/bin/annotation_security +8 -0
  12. data/lib/annotation_security/exceptions.rb +125 -0
  13. data/lib/annotation_security/exec.rb +189 -0
  14. data/lib/annotation_security/filters.rb +38 -0
  15. data/lib/annotation_security/includes/action_controller.rb +144 -0
  16. data/lib/annotation_security/includes/active_record.rb +28 -0
  17. data/lib/annotation_security/includes/helper.rb +215 -0
  18. data/lib/annotation_security/includes/resource.rb +85 -0
  19. data/lib/annotation_security/includes/role.rb +31 -0
  20. data/lib/annotation_security/includes/user.rb +27 -0
  21. data/lib/annotation_security/manager/policy_factory.rb +30 -0
  22. data/lib/annotation_security/manager/policy_manager.rb +80 -0
  23. data/lib/annotation_security/manager/relation_loader.rb +273 -0
  24. data/lib/annotation_security/manager/resource_manager.rb +36 -0
  25. data/lib/annotation_security/manager/right_loader.rb +88 -0
  26. data/lib/annotation_security/model_observer.rb +61 -0
  27. data/lib/annotation_security/policy/abstract_policy.rb +345 -0
  28. data/lib/annotation_security/policy/abstract_static_policy.rb +76 -0
  29. data/lib/annotation_security/policy/all_resources_policy.rb +21 -0
  30. data/lib/annotation_security/policy/rule.rb +340 -0
  31. data/lib/annotation_security/policy/rule_set.rb +139 -0
  32. data/lib/annotation_security/rails.rb +39 -0
  33. data/lib/annotation_security/user_wrapper.rb +74 -0
  34. data/lib/annotation_security/utils.rb +142 -0
  35. data/lib/annotation_security.rb +98 -0
  36. data/lib/extensions/action_controller.rb +33 -0
  37. data/lib/extensions/active_record.rb +35 -0
  38. data/lib/extensions/filter.rb +134 -0
  39. data/lib/extensions/object.rb +11 -0
  40. data/lib/security_context.rb +551 -0
  41. data/spec/annotation_security/exceptions_spec.rb +17 -0
  42. data/spec/annotation_security/includes/helper_spec.rb +82 -0
  43. data/spec/annotation_security/manager/policy_manager_spec.rb +15 -0
  44. data/spec/annotation_security/manager/resource_manager_spec.rb +17 -0
  45. data/spec/annotation_security/manager/right_loader_spec.rb +17 -0
  46. data/spec/annotation_security/policy/abstract_policy_spec.rb +17 -0
  47. data/spec/annotation_security/policy/all_resources_policy_spec.rb +24 -0
  48. data/spec/annotation_security/policy/rule_set_spec.rb +112 -0
  49. data/spec/annotation_security/policy/rule_spec.rb +78 -0
  50. data/spec/annotation_security/policy/test_policy_spec.rb +81 -0
  51. data/spec/annotation_security/security_context_spec.rb +78 -0
  52. data/spec/annotation_security/utils_spec.rb +74 -0
  53. data/spec/helper/test_controller.rb +66 -0
  54. data/spec/helper/test_helper.rb +5 -0
  55. data/spec/helper/test_relations.rb +7 -0
  56. data/spec/helper/test_resource.rb +39 -0
  57. data/spec/helper/test_rights.yml +5 -0
  58. data/spec/helper/test_role.rb +22 -0
  59. data/spec/helper/test_user.rb +32 -0
  60. data/spec/rails_stub.rb +38 -0
  61. data/spec/spec_helper.rb +43 -0
  62. metadata +157 -0
@@ -0,0 +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
@@ -0,0 +1,139 @@
1
+ #
2
+ # = lib/annotation_security/policy/rule_set.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::RuleSet
6
+ # Contains all rule objects for a policy
7
+ #
8
+ class AnnotationSecurity::RuleSet # :nodoc:
9
+
10
+ # Initializes the rule set
11
+ # * +pclass+ Policy class this rule set belongs to
12
+ #
13
+ def initialize(pclass)
14
+ super()
15
+ @pclass = pclass
16
+ @rights = {}
17
+ @static = {}
18
+ @dynamic = {}
19
+ end
20
+
21
+ def to_s
22
+ "<RuleSet of #@pclass>"
23
+ end
24
+
25
+ # Returns a rule object or nil if the rule does not exist.
26
+ # * +symbol+ name of the rule
27
+ # * +static+ boolean specifing whether the rule is static or dynamic
28
+ def get_rule(symbol,static)
29
+ static ? get_static_rule(symbol) : get_dynamic_rule(symbol)
30
+ end
31
+
32
+ # Returns a dynamic rule or nil if the rule does not exist.
33
+ # * +symbol+ name of the rule
34
+ def get_dynamic_rule(symbol)
35
+ # If no rule is available, maybe there is a right that can be used
36
+ @dynamic[symbol] ||= get_dynamic_right(symbol)
37
+ end
38
+
39
+ # Returns a static rule or nil if the rule does not exist.
40
+ # * +symbol+ name of the rule
41
+ def get_static_rule(symbol)
42
+ # If no rule is available, maybe there is a right that can be used
43
+ @static[symbol] ||= get_static_right(symbol)
44
+ end
45
+
46
+ # Copies a rule from another rule set.
47
+ # Returns the newly created rule or nil if the operation had no effect.
48
+ # * +symbol+ name of the rule
49
+ # * +source+ rule set to copy from
50
+ # * +static+ boolean specifing whether the rule is static or dynamic
51
+ def copy_rule_from(symbol,source,static)
52
+ add_copy(source.get_rule(symbol,static))
53
+ end
54
+
55
+ # Creates a dynamic rule that redirects to a static rule with the same name.
56
+ # Returns the newly created rule or nil if the operation had no effect.
57
+ # * +symbol+ name of the rule
58
+ def create_dynamic_copy(symbol)
59
+ rule = get_static_rule(symbol)
60
+ if rule
61
+ add_rule(symbol,
62
+ "static_policy.#{symbol}(*args)",
63
+ :resource,
64
+ :require_credential => rule.requires_credential?)
65
+ end
66
+ end
67
+
68
+ # Adds a new rule to this rule set. The rule will be classified either
69
+ # as dynamic, static, both or right.
70
+ # Returns the newly create rule.
71
+ # For an explainition of the parameters see AnnotationSecurity::Rule#initialize.
72
+ def add_rule(symbol,*args,&block)
73
+ __add__ AnnotationSecurity::Rule.new(symbol,@pclass,*args,&block)
74
+ end
75
+
76
+ private
77
+
78
+ # Copies a rule object to this rule set.
79
+ # Returns the newly created rule or nil.
80
+ # * +rule+ rule object to copy or nil.
81
+ def add_copy(rule)
82
+ __add__(rule.copy(@pclass)) if rule
83
+ end
84
+
85
+ # Adds a new rule to this rule set. The rule will be classified either
86
+ # as dynamic, static, both or right.
87
+ # * +rule+ rule object
88
+ def __add__(rule)
89
+ if rule.right?
90
+ # if the rule is a right, its not clear yet whether
91
+ # it is static or dynamic. These rules will be analyzed later.
92
+ raise_if_forbidden_name 'right', rule
93
+ raise_if_exists 'right', @rights[rule.name]
94
+ @rights[rule.name] = rule
95
+ else
96
+ raise_if_forbidden_name 'relation', rule
97
+ if rule.dynamic?
98
+ raise_if_exists 'dynamic relation', @dynamic[rule.name]
99
+ @dynamic[rule.name] = rule
100
+ end
101
+ if rule.static?
102
+ raise_if_exists 'static relation', @static[rule.name]
103
+ @static[rule.name] = rule
104
+ end
105
+ end
106
+ rule
107
+ end
108
+
109
+ # Raises an error if +rule+ is not nil.
110
+ # * +type+ type of rule, like 'right' or 'dynamic relation'
111
+ # * +rule+ existing rule object or nil
112
+ def raise_if_exists(type,rule)
113
+ raise AnnotationSecurity::RuleError.defined_twice(type,rule) if rule
114
+ end
115
+
116
+ # Raises an error if +rule+ has a forbidden name.
117
+ # * +type+ type of rule, like 'right' or 'relation'
118
+ # * +rule+ rule object
119
+ def raise_if_forbidden_name(type,rule)
120
+ if AnnotationSecurity::AbstractPolicy.forbidden_rule_names.include? rule.name.to_s
121
+ raise AnnotationSecurity::RuleError.forbidden_name(type,rule)
122
+ end
123
+ end
124
+
125
+ # Returns a dynamic rule that was defined as right
126
+ # * +symbol+ name of the rule
127
+ def get_dynamic_right(symbol)
128
+ r = @rights[symbol]
129
+ r and r.dynamic? ? r : nil
130
+ end
131
+
132
+ # Returns a static rule that was defined as right
133
+ # * +symbol+ name of the rule
134
+ def get_static_right(symbol)
135
+ r = @rights[symbol]
136
+ r and r.static? ? r : nil
137
+ end
138
+
139
+ end
@@ -0,0 +1,39 @@
1
+ #
2
+ # = annotation_security/rails/init.rb
3
+ #
4
+ # Loads the annotation security layer for a rails app
5
+
6
+ require "action_controller/dispatcher"
7
+ require "action_controller/base"
8
+
9
+ module AnnotationSecurity
10
+
11
+ # Contains rails specific initializer
12
+ class Rails
13
+ def self.init!(config)
14
+
15
+ # Policy files are situated under RAILS_ROOT/config/security
16
+ # Default policy file is internal, load it
17
+ ::AnnotationSecurity.load_relations(File.dirname(__FILE__) + '/policy/all_resources_policy')
18
+
19
+ # Add AnnotationSecurity::ModelObserver to observe changes in models.
20
+ # See http://riotprojects.com/2009/1/18/active-record-observers-in-gems-plugins
21
+ #
22
+ config.after_initialize do
23
+ # Set up a dummy security context that does not interfer with script
24
+ ::SecurityContext.initialize nil
25
+
26
+ ::ActiveRecord::Base.observers << ::AnnotationSecurity::ModelObserver
27
+
28
+ # In development mode, the models we observe get reloaded with each request. Using
29
+ # this hook allows us to reload the observer relationships each time as well.
30
+ ::ActionController::Dispatcher.to_prepare(:cache_advance_reload) do
31
+ ::AnnotationSecurity.reset
32
+ ::AnnotationSecurity::ModelObserver.instance.reload_model_observer
33
+ end
34
+ end
35
+
36
+ puts "Security layer initialized"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ #
2
+ # = lib/annotation_security/user_wrapper.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::UserWrapper
6
+ #
7
+ # This class is not in use!
8
+ #
9
+ # Needed for evaluating relations, especially if the :as-option is used.
10
+ #
11
+ # Merges a user and a role. If a role is given,
12
+ #
13
+ class AnnotationSecurity::UserWrapper # :nodoc:
14
+
15
+ # Return user wrappers for the requested role. The role(s) will be
16
+ # determined with sending user.as_'role'.
17
+ # (Normally a user has a role only once, however it will work when he
18
+ # has many roles of the same kind as well)
19
+ def self.all_for_role(user,role_name)
20
+ return [] if user.nil?
21
+ user = user.__user__ if user.is_a? AnnotationSecurity::UserWrapper
22
+ return [new(user)] if role_name.nil?
23
+ roles = user.__send__("as_#{role_name}")
24
+ return [] if roles.blank?
25
+ roles = [roles] unless roles.is_a?(Array)
26
+ roles.compact.collect { |role| new(user,role) }
27
+ end
28
+
29
+ def initialize(user,role=nil)
30
+ @user = user
31
+ @role = role
32
+ end
33
+
34
+ def id
35
+ @role? @role.id : @user.id
36
+ end
37
+
38
+ def __user__
39
+ @user
40
+ end
41
+
42
+ def __role__
43
+ @role
44
+ end
45
+
46
+ def ==(obj)
47
+ @user == obj or (!@role.nil? and @role == obj)
48
+ end
49
+
50
+ # Try to send to role, user and policy of args[0]
51
+ #
52
+ def method_missing(symbol,*args,&block)
53
+ if @role && (@role.respond_to? symbol)
54
+ @role.__send__(symbol,*args,&block)
55
+ elsif @user.respond_to? symbol
56
+ @user.__send__(symbol,*args,&block)
57
+ elsif args.first.respond_to? :policy_for
58
+ args.first.policy_for(@user).__send__(symbol,*args[1..-1])
59
+ else
60
+ # This will raise a NoMethodError
61
+ @user.__send__(symbol,*args,&block)
62
+ end
63
+ end
64
+
65
+ def is_a?(klass)
66
+ return true if super(klass)
67
+ if @role
68
+ @role.is_a?(klass)
69
+ else
70
+ @user.is_a?(klass)
71
+ end
72
+ end
73
+
74
+ end