Class: AnnotationSecurity::AbstractPolicy

Inherits:
Object
  • Object
show all
Defined in:
lib/annotation_security/policy/abstract_policy.rb

Overview

Abstract superclass for all policies

For each resource type there is a corresponding policy class. In its entire lifetime, a policy object is responsible for a single user. A policy object can validate the rights for only one resource object at a time (it is not thread safe!).

Direct Known Subclasses

AbstractStaticPolicy

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user, resource = nil) ⇒ AbstractPolicy

Initialize the instance for a user

  • user user object this policy object is responsible for

  • resource (optional) usually the resource object will be set using

    #allowed? or #with_resource
    


169
170
171
172
# File 'lib/annotation_security/policy/abstract_policy.rb', line 169

def initialize(user,resource=nil)
  @user = user
  @resource = resource
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args) ⇒ Object

Invoked by ruby when this object recieved a message it cannot handle.

  • symbol Name of the method

  • args Any arguments that were passed



302
303
304
305
306
307
308
309
310
311
312
# File 'lib/annotation_security/policy/abstract_policy.rb', line 302

def method_missing(symbol,*args) # :nodoc:

  # If possible, create the missing method and send it again
  if add_method_for_rule(symbol)
    # method was created, try again
    return __send__(symbol,*args)
  end
  
  # this will fail and an exception will be raised
  get_rule!(symbol)
end

Class Method Details

.add_rule(symbol, *args, &block) ⇒ Object

Add a rule

  • symbol rule name

  • args additional arguments

  • block code block

See AnnotationSecurity::Rule#initialize for details



161
162
163
# File 'lib/annotation_security/policy/abstract_policy.rb', line 161

def self.add_rule(symbol,*args,&block) #:nodoc:    
  rule_set.add_rule(symbol,*args,&block)
end

.all_resources_policyObject

Rules that are defined for all resource types can be found here. (Overwritten by static policy)



72
73
74
# File 'lib/annotation_security/policy/abstract_policy.rb', line 72

def self.all_resources_policy # :nodoc:
  AllResourcesPolicy
end

.classname_suffixObject

Suffix that is appended to the camlized resource type to generate a class name.



42
43
44
# File 'lib/annotation_security/policy/abstract_policy.rb', line 42

def self.classname_suffix # :nodoc:
  static? ? 'StaticPolicy' : 'Policy'
end

.copy_rule_from(symbol, source_policy) ⇒ Object

If possible, copies a rule from another policy class.

  • symbol Name of the rule

  • source_policy policy class to copy from

Returns a rule object or nil.



142
143
144
# File 'lib/annotation_security/policy/abstract_policy.rb', line 142

def self.copy_rule_from(symbol,source_policy) # :nodoc:
  rule_set.copy_rule_from(symbol,source_policy.rule_set,static?)
end

.forbidden_rule_namesObject

List of strings that are not allowed as rule names (maybe incomplete).



66
67
68
# File 'lib/annotation_security/policy/abstract_policy.rb', line 66

def self.forbidden_rule_names # :nodoc:
  instance_methods
end

.get_rule(symbol) ⇒ Object

Get a rule object

  • symbol Name of the rule



120
121
122
# File 'lib/annotation_security/policy/abstract_policy.rb', line 120

def self.get_rule(symbol) #:nodoc:
  @my_rules[symbol]
end

.has_dynamic_rule?(symbol) ⇒ Boolean

Return true iif the rule can be evaluated dynamically

  • symbol Name of the rule

Returns:

  • (Boolean)


114
115
116
# File 'lib/annotation_security/policy/abstract_policy.rb', line 114

def self.has_dynamic_rule?(symbol) # :nodoc:
  @has_dynamic[symbol]
end

.has_rule?(symbol) ⇒ Boolean

Returns true iif this policy can evaluate the rule

  • symbol Name of the rule

Returns:

  • (Boolean)


102
103
104
# File 'lib/annotation_security/policy/abstract_policy.rb', line 102

def self.has_rule?(symbol) # :nodoc:
  @has_rule[symbol]
end

.has_static_rule?(symbol) ⇒ Boolean

Returns true iif the rule can be evaluated statically

  • symbol Name of the rule

Returns:

  • (Boolean)


108
109
110
# File 'lib/annotation_security/policy/abstract_policy.rb', line 108

def self.has_static_rule?(symbol) # :nodoc:
  static_policy_class.has_rule? symbol
end

.initialize(resource_type) ⇒ Object

Initializes a subclass of AbstractPolicy



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/annotation_security/policy/abstract_policy.rb', line 24

def self.initialize(resource_type) #:nodoc:
  @resource_type = resource_type.to_s.underscore.to_sym
  
  # register the class as constant
  name = resource_type.to_s.camelize + classname_suffix
  Object.const_set name, self

  unless static?
    # Each policy has a static partner
    @static_policy_class = AnnotationSecurity::AbstractStaticPolicy.new_subclass(@resource_type)
    @static_policy_class.belongs_to self
    reset
  end
end

.load_rule(symbol) ⇒ Object

The rule symbol was requested, try to find and load it. Returns a rule object or nil.



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/annotation_security/policy/abstract_policy.rb', line 126

def self.load_rule(symbol) #:nodoc:
  # 1. Have a look in the rule set
  # 2. Maybe the rule is defined for all resources
  # 3. Redirect the rule to the static side
  r = rule_set.get_rule(symbol,static?) ||
      copy_rule_from(symbol,all_resources_policy) ||
      use_static_rule(symbol)
  # Create a method for the rule
  r.extend_class(self) if r
  r
end

.new_subclass(resource_type) ⇒ Object

Creates a new policy class for a resource type.



16
17
18
19
20
# File 'lib/annotation_security/policy/abstract_policy.rb', line 16

def self.new_subclass(resource_type) #:nodoc:
  Class.new(self).tap do |c|
    c.initialize(resource_type)
  end
end

.resetObject

(Re-)Initializes the policy class. Removes all generated methods and clears the rule set.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/annotation_security/policy/abstract_policy.rb', line 49

def self.reset # :nodoc:
  instance_methods(false).each { |m| remove_method m }
  @has_rule = Hash.new {|h,k| h[k] = !get_rule(k).nil?}
  @my_rules = Hash.new {|h,k| h[k] = load_rule(k)}
  unless static?
    # set of all rule objects available for this policy
    @rule_set = AnnotationSecurity::RuleSet.new(self)
    # {:rule => boolean} if true, the rule can be evaluated statically only
    @static_only = Hash.new(false)
    # {:rule => boolean} if true, the rule can be evaluated dynamically
    @has_dynamic = Hash.new {|h,k| h[k] = has_rule?(k) && !@static_only[k]}
    @static_policy_class.reset
  end
end

.resource_typeObject

Symbol representing the resource type this policy is responsible for.



78
79
80
# File 'lib/annotation_security/policy/abstract_policy.rb', line 78

def self.resource_type # :nodoc:
  @resource_type
end

.rule_setObject

Rule set for this classes resource type



96
97
98
# File 'lib/annotation_security/policy/abstract_policy.rb', line 96

def self.rule_set # :nodoc:
  @rule_set
end

.static?Boolean

Returns true iif this is policy class is responsible for static rules.

Returns:

  • (Boolean)


90
91
92
# File 'lib/annotation_security/policy/abstract_policy.rb', line 90

def self.static? # :nodoc:
  false
end

.static_policy_classObject

The corresponding static policy class.



84
85
86
# File 'lib/annotation_security/policy/abstract_policy.rb', line 84

def self.static_policy_class # :nodoc:
  @static_policy_class
end

.use_static_rule(symbol) ⇒ Object

If possible, redirects the rule to the static side. Returns a rule object or nil.



148
149
150
151
152
153
# File 'lib/annotation_security/policy/abstract_policy.rb', line 148

def self.use_static_rule(symbol) #:nodoc:
  if has_static_rule?(symbol)
    @static_only[symbol] = true
    rule_set.create_dynamic_copy(symbol)
  end
end

Instance Method Details

#add_method_for_rule(symbol) ⇒ Object

Return true if it was possible to create a method for the rule

  • symbol Name of the method



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/annotation_security/policy/abstract_policy.rb', line 316

def add_method_for_rule(symbol) # :nodoc:
  # Check if symbol is a known rule. If available, it will be loaded and
  # a method will be created automatically.
  if has_rule?(symbol)
    # method was created
    return true
  else
    # Remove prefix or suffix if available
    cleaned = AnnotationSecurity::Utils.method_body(symbol)
    if cleaned
      # Redirect to the cleaned method
      self.class.class_eval "def #{symbol}(*args); #{cleaned}(*args); end"
      return true
    end
  end
  # Hopeless
  false
end

#all_for_role(user, role_name) ⇒ Object

Return objects for the requested role. The role(s) will be determined with sending user.as_’role’. (Normally a user has a role only once, however it will work when he has many roles of the same kind as well)



281
282
283
284
285
286
287
288
289
# File 'lib/annotation_security/policy/abstract_policy.rb', line 281

def all_for_role(user,role_name) # :nodoc:
  return [] if user.nil?
  # is it possible that user is a role? if so, implement conversion to user
  return [user] if role_name.nil?
  roles = user.__send__("as_#{role_name}")
  return [] if roles.blank?
  roles = [roles] unless roles.is_a?(Array)
  roles.compact
end

#allowed?(right, resource_obj, *args) ⇒ Boolean

Returns true iif the user has the right for resource_obj

  • right symbol

  • resource_obj resource object to test the right for

  • args (optional) additional arguments passed when evaluating the right

This is not thread safe! Don’t share policy objects between different threads (should be no problem though).

Example

policy.allowed? :show, obj #=> true or false

Returns:

  • (Boolean)


193
194
195
196
197
198
199
# File 'lib/annotation_security/policy/abstract_policy.rb', line 193

def allowed?(right,resource_obj,*args)
  @resource = resource_obj
  __send__(right,*args)
#  rescue
#    raise "#{$!} in #{resource_type} policy " +
#      "during rule #{right} of #{resource_obj} with args [#{args.join(", ")}]"
end

#evaluate(rules) ⇒ Object

Evalutates all rules.

  • rules array of symbols

Throws a SecurityViolationError if a rule fails, returns true if all rules succeed.



236
237
238
239
240
241
242
243
# File 'lib/annotation_security/policy/abstract_policy.rb', line 236

def evaluate(rules) #:nodoc:
  rules.each do |rule|
    unless __send__(rule)
      raise_access_denied(rule)
    end
  end
  true
end

#evaluate_dynamically(rules) ⇒ Object

Evaluate the rules in dynamic mode. Rules that cannot be evaluated are skipped.

  • rules array of symbols

Throws a SecurityViolationError if a rule fails, returns true if all rules succeed.



223
224
225
226
227
228
229
230
# File 'lib/annotation_security/policy/abstract_policy.rb', line 223

def evaluate_dynamically(rules) #:nodoc:
  rules.each do |rule|
    if self.class.has_dynamic_rule?(rule) && !__send__(rule)
      raise_access_denied(rule)
    end
  end
  true
end

#evaluate_rule(symbol, user, args) ⇒ Object

Evaluate a rule that is defined with a proc

  • symbol Name of the rule

  • user user object that has to fulfill the rule

  • args List of additional arguments



295
296
297
# File 'lib/annotation_security/policy/abstract_policy.rb', line 295

def evaluate_rule(symbol,user,args) # :nodoc:
  get_rule!(symbol).evaluate(self,user,@resource,*args)
end

#evaluate_statically(rules) ⇒ Object

Evaluate the rules in static mode. Rules that cannot be evaluated are skipped.

  • rules array of symbols

Throws a SecurityViolationError if a rule fails, returns true if all rules succeed.



214
215
216
# File 'lib/annotation_security/policy/abstract_policy.rb', line 214

def evaluate_statically(rules) #:nodoc:
  static_policy.evaluate_statically(rules)
end

#get_rule(symbol) ⇒ Object

Returns a rule object or nil if it does not exist

  • symbol Name of the rule



259
260
261
# File 'lib/annotation_security/policy/abstract_policy.rb', line 259

def get_rule(symbol) # :nodoc:
  self.class.get_rule(symbol)
end

#get_rule!(symbol) ⇒ Object

Returns a rule object or raises an exception.

  • symbol Name of the rule



253
254
255
# File 'lib/annotation_security/policy/abstract_policy.rb', line 253

def get_rule!(symbol) # :nodoc:
  get_rule(symbol) or raise_rule_missing(symbol)
end

#has_rule?(symbol) ⇒ Boolean

Returns true iif this policy can evaluate this rule

  • symbol Name of the rule

Returns:

  • (Boolean)


247
248
249
# File 'lib/annotation_security/policy/abstract_policy.rb', line 247

def has_rule?(symbol)
  self.class.has_rule? symbol
end

#raise_access_denied(rule) ⇒ Object

:nodoc:



341
342
343
# File 'lib/annotation_security/policy/abstract_policy.rb', line 341

def raise_access_denied(rule) #:nodoc:
  SecurityContext.raise_access_denied(rule,resource_type,@resource)
end

#raise_rule_missing(symbol) ⇒ Object

Raises an error saying that a rule could not be found this policy class

  • symbol Name of the rule



337
338
339
# File 'lib/annotation_security/policy/abstract_policy.rb', line 337

def raise_rule_missing(symbol) # :nodoc:
  raise AnnotationSecurity::RuleNotFoundError.for_rule(symbol,self.class)
end

#resource_typeObject

Symbol representing the resource type this policy is responsible for.



181
182
183
# File 'lib/annotation_security/policy/abstract_policy.rb', line 181

def resource_type # :nodoc:
  self.class.resource_type
end

#static_policyObject

Static policy object to evaluate the static rules



175
176
177
# File 'lib/annotation_security/policy/abstract_policy.rb', line 175

def static_policy # :nodoc:
  @static_policy ||= self.class.static_policy_class.new(@user)
end

#user_roles(symbol, require_user) ⇒ Object

Returns a list of user wrappers for a role. See #all_for_role for details.

  • symbol Name of the role

  • require_user (boolean) Indicating if the rule that requested the roles requires a user for evaluation. If @user is nil and require_user is true, an empty array is returned, which will make the rule fail immediately. If require_user is false, an array containing nil is returned and the rule will be evaluated once (with nil as current user).



271
272
273
274
275
# File 'lib/annotation_security/policy/abstract_policy.rb', line 271

def user_roles(symbol,require_user) # :nodoc:
  return [nil] if @user.nil? && !require_user
  # AnnotationSecurity::UserWrapper.all_for_role(@user,symbol)
  all_for_role(@user,symbol)
end

#with_resource(resource_obj) ⇒ Object

Sets the resource object and returns self

Example

policy.with_resource(obj).show? #=> true or false


204
205
206
207
# File 'lib/annotation_security/policy/abstract_policy.rb', line 204

def with_resource(resource_obj)
  @resource = resource_obj
  self
end