Class: SecurityContext

Inherits:
Object show all
Defined in:
lib/security_context.rb

Overview

SecurityContext

The SecurityContext provides methods for all security concerns of the current request.

For every request, it has to be initialized using #current_user=. It is recommended to do this in a security filter, which can be used to catch AnnotationSecurityExceptions as well.

The SecurityContext is implemented as a singleton for the current thread. Thus, all instance methods can be send to the class as well.

Defined Under Namespace

Classes: SecurityContextDummy

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(controller) ⇒ SecurityContext

Initialize context for the given controller



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/security_context.rb', line 67

def initialize(controller) # :nodoc:
  super()

  @controller = controller

  # initialize rule

  # rules that are not bound to any source,
  # will be triggered by model observer
  @context_rules = new_rules_hash
  @valid_objects = new_valid_objects_hash

  # rules bound to request param
  @param_rules = new_bound_rules_hash
  @param_valid_objects = new_bound_valid_objects_hash

  # rules bound to variable
  @var_rules = new_bound_rules_hash
  @var_valid_objects = new_bound_valid_objects_hash

  # Hash with all required policies
  @policies = new_policy_hash
end

Class Method Details

.currentObject

Returns current security context



23
24
25
# File 'lib/security_context.rb', line 23

def self.current
  Thread.current[:security_context]
end

.ignore_security!Object

Disables all security checkings. Is only available in development mode.



55
56
57
58
59
# File 'lib/security_context.rb', line 55

def self.ignore_security!
  security_methods.each do |method|
    class_eval "def self.#{method}(*args); true; end"
  end
end

.initialize(controller) ⇒ Object

At the begin of a request, the security context will be initialized for the current controller.



30
31
32
# File 'lib/security_context.rb', line 30

def self.initialize(controller) # :nodoc:
   load(new(controller))
end

.load(sec_context) ⇒ Object

As the security context is a singleton bound to the current thread, it will not be available in other threads. The following example shows how to use the security context inside of a spawn block

copy = SecurityContext.copy
spawn do
  SecurityContext.load(copy)
  begin
    # ...
  rescue SecurityViolationError
    puts 'Security was violated'
  end
end


48
49
50
# File 'lib/security_context.rb', line 48

def self.load(sec_context)
  Thread.current[:security_context] = sec_context
end

.raise_access_denied(*policy_args) ⇒ Object

Raise a SecurityViolationError. See allowed? for details on policy_args.



322
323
324
325
# File 'lib/security_context.rb', line 322

def self.raise_access_denied(*policy_args)
  log_access_denied(policy_args)
  raise SecurityViolationError.access_denied(credential,*policy_args)
end

.without_security!(&block) ⇒ Object

Runs a given block with security disabled. Inside the block, the context will be disabled for the current thread.



564
565
566
567
568
569
570
571
# File 'lib/security_context.rb', line 564

def self.without_security!(&block)
  old_current = current

  load(SecurityContextDummy.new(old_current.credential))
  return_value = yield
  load old_current
  return_value
end

Instance Method Details

#allow_action?(*args) ⇒ Boolean

Checks the rules of an other action. Note that rules that are bound to a variable can not be checked.

Parameters

  • controller Symbol representing the controller, like :resource

  • action The called action, like :update

  • objects (optional) List of objects that will be relevant for that action.

  • params (optional) Hash of the passed parameters, like :id => 1.

Examples

Checks static and pretest rules.

allow_action? :resource, :create
# => true if the current user may execute ResourcesController#create

Checks static, pretest and context rules

allow_action? :resource, :edit, [@resource]
# => true if the current user may execute ResourcesController#edit,
#    assuming that @resource will be used in that action

Checks static, pretest and context rules and all rules that are bound to :id.

allow_action? :resource, :edit, [@resource], {:id => 4}
# => true if the current user may execute ResourcesController#edit,
#    assuming that @resource will be used in that action

Returns:

  • (Boolean)


252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/security_context.rb', line 252

def allow_action?(*args) # :nodoc:

  controller, action, objects, params =
      AnnotationSecurity::Utils.parse_action_args(args)

  # var rules are ignored here
  context_rules, param_rules, _ = get_rule_set(controller, action)

  # check static rules
  evaluate_statically(context_rules)

  # check context rules for all objects
  objects.each do |o|
    res_type = o.resource_type
    evaluate_context_rules(context_rules, res_type, o)
  end

  evaluate_bound_rules_for_params(param_rules, params)

  true
rescue SecurityViolationError
  return false
end

#allowed?(*policy_args) ⇒ Boolean Also known as: is?

Returns true iif the operation defined by policy_args is allowed.

The following calls to #allowed? are allowed:

allowed? :show, :resource, @resource
# => true if the current user has the right to show @resource,
#    which belongs to the :resource resource-class

In case of model objects or other classes which implement a #resource_type method the the second argument may be ommited

allowed? :show, @resource
# equivalent to the above call if @resource.resource_type == :resource

A policy description used as a controller annotation may also be to check a right

allowed? "show resource", @resource
# => true if the current user has the right "show resource" for @resource

A policy may also be applied without an object representing the context:

allowed? :show, :resource
# => true if the current may show resources.

This will only check system and pretest rules. The result true does not mean that the user may show all resources. However, a false indicates that the user is not allowed to show any resources.

If the resource type is omitted as well, only rules defined for all resources can be tested. See RelationLoader#all_resources for details.

allowed? :administrate
# => true if the user is allowed to administrate all resources.

Returns:

  • (Boolean)


206
207
208
209
# File 'lib/security_context.rb', line 206

def allowed?(*policy_args)
  policy_args = AnnotationSecurity::Utils.parse_policy_arguments(policy_args)
  __allowed?(*policy_args)
end

#apply_context_rules(*res_args) ⇒ Object Also known as: apply_rules

Applies all rules of the current action to the resource defined by resource_args. Raises a SecurityViolationError if a rule is violated.



295
296
297
298
# File 'lib/security_context.rb', line 295

def apply_context_rules(*res_args) # :nodoc:
  restype, res = AnnotationSecurity::Utils.parse_resource_arguments(res_args)
  evaluate_context_rules(@context_rules, restype, res)
end

#apply_param_rulesObject

:nodoc:



283
284
285
# File 'lib/security_context.rb', line 283

def apply_param_rules # :nodoc:
  evaluate_bound_rules(@param_rules, @param_valid_objects)
end

#apply_rule(*args) ⇒ Object

Raises a SecurityViolationError if the rule defined by policy_args is not allowed. See allowed? for details.



222
223
224
# File 'lib/security_context.rb', line 222

def apply_rule(*args)
  self.class.raise_access_denied(*args) unless allowed?(*args)
end

#apply_rules_after_actionObject

:nodoc:



166
167
168
169
# File 'lib/security_context.rb', line 166

def apply_rules_after_action # :nodoc:
  # check again, bindings may have been changed
  apply_var_rules
end

#apply_rules_before_actionObject

:nodoc:



158
159
160
161
162
163
164
# File 'lib/security_context.rb', line 158

def apply_rules_before_action # :nodoc:
  # apply static rules before entering the action
  apply_static_rules
  # bindings may apply to parameters, try to check them too
  apply_param_rules
  apply_var_rules
end

#apply_static_rulesObject

Applies all system and pretest rules of the current action. Raises a SecurityViolationError if a rule is violated.



279
280
281
# File 'lib/security_context.rb', line 279

def apply_static_rules # :nodoc:
  evaluate_statically(@context_rules)
end

#apply_var_rulesObject

:nodoc:



287
288
289
# File 'lib/security_context.rb', line 287

def apply_var_rules # :nodoc:
  evaluate_bound_rules(@var_rules, @var_valid_objects)
end

#copyObject

Creates a copy of the current security context. See #load for more information.



114
115
116
# File 'lib/security_context.rb', line 114

def copy
  returning self.class.new(@controller) { |sc| sc.credential = credential }
end

#credentialObject Also known as: current_credential

Get the current credential



105
106
107
# File 'lib/security_context.rb', line 105

def credential
  @credential
end

#credential=(user) ⇒ Object Also known as: current_credential=

Sets the current user. This has to be done in a before or around filter, before entering the action. Elsewise, the user will be interpreted as not being logged in. Once set, the current user cannot be changed.



95
96
97
98
99
100
101
102
# File 'lib/security_context.rb', line 95

def credential=(user)
  if @cred_set
    raise AnnotationSecurity::AnnotationSecurityError,
          "Credential already set for this request"
  end
  @cred_set = true
  @credential = user
end

#enabled?Boolean

Returns:

  • (Boolean)


557
558
559
# File 'lib/security_context.rb', line 557

def enabled?
  true
end

#eval_with_security(rules) ⇒ Object

Evaluates the given block, additionally using the given rules.

rules == [ { :action => action, :resource => res_type, :source => binding  }, ...]

action and res_type should be symbols, binding is optional



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/security_context.rb', line 140

def eval_with_security(rules)
  install_rules(rules)

  apply_rules_before_action

  result = yield

  apply_rules_after_action

  result
rescue AnnotationSecurity::SecurityError
  SecurityContext.security_exception = $!
  raise $!
ensure
  uninstall_rules(rules)
  result
end

#log!(&proc) ⇒ Object

Activates access logging for the current request.



329
330
331
332
333
334
335
336
# File 'lib/security_context.rb', line 329

def log!(&proc)
  @enable_logging = true
  @log = proc || Proc.new do |result, action, res_type, resource|
    result = result ? 'ALLOWED' : 'REFUSED' unless result.is_a? String
    msg = "%-8s %-10s %-16s %s" % [result, action, res_type, resource]
    puts msg
  end
end

#log_access_denied(policy_args) ⇒ Object

:nodoc:



338
339
340
# File 'lib/security_context.rb', line 338

def log_access_denied(policy_args) # :nodoc:
  @log.call('DENIED!', *policy_args) if @enable_logging
end

#observe(*resource_args) ⇒ Object

Call if a resource object was touched during an action. Will be called automatically for model objects.

Applies all rules that are currently active to the resource defined by resource_args. Raises a SecurityViolationError if a rule is violated.

Usage

observe :resource, @resource

where :resource is the resource type @resource belongs to, or

observe @resource

which is equivalent if @resource.resource_name == :resource



315
316
317
# File 'lib/security_context.rb', line 315

def observe(*resource_args)
  apply_context_rules(*resource_args)
end

#security_exceptionObject

If the action was aborted due to a security exception, this returns the exception that was raised. Returns nil if no exception occurred.



127
128
129
# File 'lib/security_context.rb', line 127

def security_exception
  @security_exception
end

#security_exception=(ex) ⇒ Object

Will be set if an security exception was catched by the security filter



119
120
121
122
# File 'lib/security_context.rb', line 119

def security_exception=(ex) # :nodoc:
  @security_exception = ex
  @controller.security_exception = ex
end

#send_with_security(rules, obj, msg, *args, &proc) ⇒ Object

See eval_with_security.



132
133
134
# File 'lib/security_context.rb', line 132

def send_with_security(rules, obj, msg, *args, &proc)
  eval_with_security(rules) { obj.send(msg, *args, &proc) }
end