Class: Authorization::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/declarative_authorization/authorization.rb

Overview

Authorization::Engine implements the reference monitor. It may be used for querying the permission and retrieving obligations under which a certain privilege is granted for the current user.

Defined Under Namespace

Classes: AttributeValidator

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(reader = nil) ⇒ Engine

If reader is not given, a new one is created with the default authorization configuration of AUTH_DSL_FILE. If given, may be either a Reader object or a path to a configuration file.



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
# File 'lib/declarative_authorization/authorization.rb', line 67

def initialize (reader = nil)
  if reader.nil?
    begin
      reader = Reader::DSLReader.load(AUTH_DSL_FILE)
    rescue SystemCallError
      reader = Reader::DSLReader.new
    end
  elsif reader.is_a?(String)
    reader = Reader::DSLReader.load(reader)
  end
  @privileges = reader.privileges_reader.privileges
  # {priv => [[priv, ctx],...]}
  @privilege_hierarchy = reader.privileges_reader.privilege_hierarchy
  @auth_rules = reader.auth_rules_reader.auth_rules
  @roles = reader.auth_rules_reader.roles
  @role_hierarchy = reader.auth_rules_reader.role_hierarchy

  @role_titles = reader.auth_rules_reader.role_titles
  @role_descriptions = reader.auth_rules_reader.role_descriptions
  @reader = reader
  
  # {[priv, ctx] => [priv, ...]}
  @rev_priv_hierarchy = {}
  @privilege_hierarchy.each do |key, value|
    value.each do |val| 
      @rev_priv_hierarchy[val] ||= []
      @rev_priv_hierarchy[val] << key
    end
  end
  @rev_role_hierarchy = {}
  @role_hierarchy.each do |higher_role, lower_roles|
    lower_roles.each do |role|
      (@rev_role_hierarchy[role] ||= []) << higher_role
    end
  end
end

Instance Attribute Details

#auth_rulesObject (readonly)

Returns the value of attribute auth_rules.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def auth_rules
  @auth_rules
end

#privilege_hierarchyObject (readonly)

Returns the value of attribute privilege_hierarchy.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def privilege_hierarchy
  @privilege_hierarchy
end

#privilegesObject (readonly)

Returns the value of attribute privileges.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def privileges
  @privileges
end

#rev_priv_hierarchyObject (readonly)

Returns the value of attribute rev_priv_hierarchy.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def rev_priv_hierarchy
  @rev_priv_hierarchy
end

#rev_role_hierarchyObject (readonly)

Returns the value of attribute rev_role_hierarchy.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def rev_role_hierarchy
  @rev_role_hierarchy
end

#role_descriptionsObject (readonly)

Returns the value of attribute role_descriptions.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def role_descriptions
  @role_descriptions
end

#role_hierarchyObject (readonly)

Returns the value of attribute role_hierarchy.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def role_hierarchy
  @role_hierarchy
end

#role_titlesObject (readonly)

Returns the value of attribute role_titles.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def role_titles
  @role_titles
end

#rolesObject (readonly)

Returns the value of attribute roles.



60
61
62
# File 'lib/declarative_authorization/authorization.rb', line 60

def roles
  @roles
end

Class Method Details

.instance(dsl_file = nil) ⇒ Object

Returns an instance of Engine, which is created if there isn’t one yet. If dsl_file is given, it is passed on to Engine.new and a new instance is always created.



252
253
254
255
256
257
258
# File 'lib/declarative_authorization/authorization.rb', line 252

def self.instance (dsl_file = nil)
  if dsl_file or ENV['RAILS_ENV'] == 'development'
    @@instance = new(dsl_file)
  else
    @@instance ||= new
  end
end

Instance Method Details

#description_for(role) ⇒ Object

Returns the description for the given role. The description may be specified with the authorization rules. Returns nil if none was given.



216
217
218
# File 'lib/declarative_authorization/authorization.rb', line 216

def description_for (role)
  role_descriptions[role]
end

#initialize_copy(from) ⇒ Object

:nodoc:



104
105
106
107
108
109
110
# File 'lib/declarative_authorization/authorization.rb', line 104

def initialize_copy (from) # :nodoc:
  [
    :privileges, :privilege_hierarchy, :roles, :role_hierarchy, :role_titles,
    :role_descriptions, :rev_priv_hierarchy, :rev_role_hierarchy
  ].each {|attr| instance_variable_set(:"@#{attr}", from.send(attr).clone) }
  @auth_rules = from.auth_rules.collect {|rule| rule.clone}
end

#obligations(privilege, options = {}) ⇒ Object

Returns the obligations to be met by the current user for the given privilege as an array of obligation hashes in form of

[{:object_attribute => obligation_value, ...}, ...]

where obligation_value is either (recursively) another obligation hash or a value spec, such as

[operator, literal_value]

The obligation hashes in the array should be OR’ed, conditions inside the hashes AND’ed.

Example

{:branch => {:company => [:is, 24]}, :active => [:is, true]}

Options

:context

See permit!

:user

See permit!



204
205
206
207
208
209
210
211
# File 'lib/declarative_authorization/authorization.rb', line 204

def obligations (privilege, options = {})
  options = {:context => nil}.merge(options)
  user, roles, privileges = user_roles_privleges_from_options(privilege, options)
  attr_validator = AttributeValidator.new(self, user, nil, privilege, options[:context])
  matching_auth_rules(roles, privileges, options[:context]).collect do |rule|
    rule.obligations(attr_validator)
  end.flatten
end

#permit!(privilege, options = {}) ⇒ Object

Returns true if privilege is met by the current user. Raises AuthorizationError otherwise. privilege may be given with or without context. In the latter case, the :context option is required.

Options:

:context

The context part of the privilege. Defaults either to the table_name of the given :object, if given. That is, either :users for :object of type User.

Raises AuthorizationUsageError if context is missing and not to be infered.

:object

An context object to test attribute checks against.

:skip_attribute_test

Skips those attribute checks in the authorization rules. Defaults to false.

:user

The user to check the authorization for. Defaults to Authorization#current_user.



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
# File 'lib/declarative_authorization/authorization.rb', line 132

def permit! (privilege, options = {})
  return true if Authorization.ignore_access_control
  options = {
    :object => nil,
    :skip_attribute_test => false,
    :context => nil
  }.merge(options)
  
  # Make sure we're handling all privileges as symbols.
  privilege = privilege.is_a?( Array ) ?
              privilege.flatten.collect { |priv| priv.to_sym } :
              privilege.to_sym
  
  #
  # If the object responds to :proxy_reflection, we're probably working with
  # an association proxy.  Use 'new' to leverage ActiveRecord's builder
  # functionality to obtain an object against which we can check permissions.
  #
  # Example: permit!( :edit, :object => user.posts )
  #
  if options[:object].respond_to?( :proxy_reflection ) && options[:object].respond_to?( :new )
    options[:object] = options[:object].new
  end
  
  options[:context] ||= options[:object] && options[:object].class.table_name.to_sym rescue NoMethodError
  
  user, roles, privileges = user_roles_privleges_from_options(privilege, options)

  # find a authorization rule that matches for at least one of the roles and 
  # at least one of the given privileges
  attr_validator = AttributeValidator.new(self, user, options[:object], privilege, options[:context])
  rules = matching_auth_rules(roles, privileges, options[:context])
  if rules.empty?
    raise NotAuthorized, "No matching rules found for #{privilege} for #{user.inspect} " +
      "(roles #{roles.inspect}, privileges #{privileges.inspect}, " +
      "context #{options[:context].inspect})."
  end
  
  # Test each rule in turn to see whether any one of them is satisfied.
  unless rules.any? {|rule| rule.validate?(attr_validator, options[:skip_attribute_test])}
    raise AttributeAuthorizationError, "#{privilege} not allowed for #{user.inspect} on #{(options[:object] || options[:context]).inspect}."
  end
  true
end

#permit?(privilege, options = {}, &block) ⇒ Boolean

Calls permit! but rescues the AuthorizationException and returns false instead. If no exception is raised, permit? returns true and yields to the optional block.

Returns:

  • (Boolean)


180
181
182
183
184
185
186
# File 'lib/declarative_authorization/authorization.rb', line 180

def permit? (privilege, options = {}, &block) # :yields:
  permit!(privilege, options)
  yield if block_given?
  true
rescue NotAuthorized
  false
end

#roles_for(user) ⇒ Object

Returns the role symbols of the given user.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/declarative_authorization/authorization.rb', line 228

def roles_for (user)
  raise AuthorizationUsageError, "User object doesn't respond to roles" \
    if !user.respond_to?(:role_symbols) and !user.respond_to?(:roles)

  RAILS_DEFAULT_LOGGER.info("The use of user.roles is deprecated.  Please add a method " +
      "role_symbols to your User model.") if defined?(RAILS_DEFAULT_LOGGER) and !user.respond_to?(:role_symbols)

  roles = user.respond_to?(:role_symbols) ? user.role_symbols : user.roles

  raise AuthorizationUsageError, "User.#{user.respond_to?(:role_symbols) ? 'role_symbols' : 'roles'} " +
    "doesn't return an Array of Symbols (#{roles.inspect})" \
        if !roles.is_a?(Array) or (!roles.empty? and !roles[0].is_a?(Symbol))

  (roles.empty? ? [:guest] : roles)
end

#roles_with_hierarchy_for(user) ⇒ Object

Returns the role symbols and inherritted role symbols for the given user



245
246
247
# File 'lib/declarative_authorization/authorization.rb', line 245

def roles_with_hierarchy_for(user)
  flatten_roles(roles_for(user))
end

#title_for(role) ⇒ Object

Returns the title for the given role. The title may be specified with the authorization rules. Returns nil if none was given.



223
224
225
# File 'lib/declarative_authorization/authorization.rb', line 223

def title_for (role)
  role_titles[role]
end