class Authorization::Engine
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.
Attributes
Public Class Methods
# File lib/declarative_authorization/authorization.rb, line 284 def self.development_reload? if Rails.env.development? mod_time = AUTH_DSL_FILES.map do |m| begin File.mtime(m) rescue Time.at(0) end end.flatten.max @@auth_dsl_last_modified ||= mod_time if mod_time > @@auth_dsl_last_modified @@auth_dsl_last_modified = mod_time return true end end end
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.
# File lib/declarative_authorization/authorization.rb, line 304 def self.instance(dsl_file = nil) if dsl_file or development_reload? @@instance = new(dsl_file) else @@instance ||= new end end
If reader
is not given, a new one is created with the default authorization configuration of AUTH_DSL_FILES
. If given, may be either a Reader
object or a path to a configuration file.
# File lib/declarative_authorization/authorization.rb, line 87 def initialize(reader = nil) #@auth_rules = AuthorizationRuleSet.new reader.auth_rules_reader.auth_rules @reader = Reader::DSLReader.factory(reader || AUTH_DSL_FILES) end
Public Instance Methods
Returns the description for the given role. The description may be specified with the authorization rules. Returns nil
if none was given.
# File lib/declarative_authorization/authorization.rb, line 250 def description_for(role) role_descriptions[role] end
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!
# File lib/declarative_authorization/authorization.rb, line 233 def obligations(privilege, options = {}) options = {:context => nil}.merge(options) user, roles, privileges = user_roles_privleges_from_options(privilege, options) permit!(privilege, :skip_attribute_test => true, :user => user, :context => options[:context]) return [] if roles.is_a?(Hash) && !(roles.keys & omnipotent_roles).empty? 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
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 tableized
class_name
of the given :object
, if given. That is, :users
for :object
of type User. RaisesAuthorizationUsageError
if context is missing and not to be inferred. - :
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.
- :
bang
-
Should
NotAuthorized
exceptions be raised Defaults to true.
# File lib/declarative_authorization/authorization.rb, line 145 def permit!(privilege, options = {}) return true if Authorization.ignore_access_control options = { :object => nil, :skip_attribute_test => false, :context => nil, :bang => true }.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 Authorization.is_a_association_proxy?(options[:object]) && options[:object].respond_to?(:new) options[:object] = options[:object].where(nil).new end options[:context] ||= options[:object] && ( options[:object].class.respond_to?(:decl_auth_context) ? options[:object].class.decl_auth_context : options[:object].class.name.tableize.to_sym ) rescue NoMethodError user, roles, privileges = user_roles_privleges_from_options(privilege, options) callback = Rails.application.config.try(:ae_declarative_authorization_permit_callback) callback.call(controller: options[:controller], privilege: privilege) if callback && options.include?(:controller) return true if roles.is_a?(Hash) && !(roles.keys & omnipotent_roles).empty? # 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]) # Test each rule in turn to see whether any one of them is satisfied. rules.each do |rule| return true if rule.validate?(attr_validator, options[:skip_attribute_test]) end if options[:bang] if rules.empty? raise NotAuthorized, "No matching rules found for #{privilege} for User with id #{user.try(:id)} " + "(roles #{roles.inspect}, privileges #{privileges.inspect}, " + "context #{options[:context].inspect})." else raise AttributeAuthorizationError, "#{privilege} not allowed for User with id #{user.try(:id)} on #{(options[:object] || options[:context]).inspect}." end else false end end
Calls permit! but doesn't raise authorization errors. If no exception is raised, permit? returns true and yields to the optional block.
# File lib/declarative_authorization/authorization.rb, line 208 def permit?(privilege, options = {}) # :yields: if permit!(privilege, options.merge(:bang=> false)) yield if block_given? true else false end end
{[priv, ctx] => [priv, …]}
# File lib/declarative_authorization/authorization.rb, line 97 def rev_priv_hierarchy if @rev_priv_hierarchy.nil? @rev_priv_hierarchy = {} privilege_hierarchy.each do |key, value| value.each do |val| @rev_priv_hierarchy[val] ||= [] @rev_priv_hierarchy[val] << key end end end @rev_priv_hierarchy end
{[priv, ctx] => [priv, …]}
# File lib/declarative_authorization/authorization.rb, line 111 def rev_role_hierarchy if @rev_role_hierarchy.nil? @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 @rev_role_hierarchy end
Returns the role symbols of the given user.
# File lib/declarative_authorization/authorization.rb, line 262 def roles_for(user) user ||= Authorization.current_user raise AuthorizationUsageError, "User object doesn't respond to roles (#{user.try(:id)})" \ if !user.respond_to?(:role_symbols) and !user.respond_to?(:roles) Rails.logger.info("The use of user.roles is deprecated. Please add a method " + "role_symbols to your User model.") if defined?(Rails) and Rails.respond_to?(: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? ? [Authorization.default_role] : roles) end
Returns the role symbols and inherritted role symbols for the given user
# File lib/declarative_authorization/authorization.rb, line 280 def roles_with_hierarchy_for(user) flatten_roles(roles_for(user)) end
Returns the title for the given role. The title may be specified with the authorization rules. Returns nil
if none was given.
# File lib/declarative_authorization/authorization.rb, line 257 def title_for(role) role_titles[role] end
Private Instance Methods
Returns the privilege hierarchy flattened for given privileges in context.
# File lib/declarative_authorization/authorization.rb, line 365 def flatten_privileges(privileges, context = nil) # TODO: caching? raise AuthorizationUsageError, 'No context given or inferable from object' unless context hierarchy = rev_priv_hierarchy flattened_privileges = privileges.clone flattened_privileges.each do |priv| flattened_privileges.concat(hierarchy[[priv, nil]]) if hierarchy[[priv, nil]] flattened_privileges.concat(hierarchy[[priv, context]]) if hierarchy[[priv, context]] end flattened_privileges.uniq end
# File lib/declarative_authorization/authorization.rb, line 351 def flatten_roles(roles, ignore: []) # TODO: caching? hierarchy = role_hierarchy flattened_roles = {} roles.each do |role| flattened_roles[role] = true children = (hierarchy[role] || []) - ignore flattened_roles.merge!(flatten_roles(children, ignore: ignore + [role])) if children.any? end flattened_roles end
# File lib/declarative_authorization/authorization.rb, line 378 def matching_auth_rules(roles, privileges, context) auth_rules.matching(roles, privileges, context) end
# File lib/declarative_authorization/authorization.rb, line 334 def user_roles_privleges_from_options(privilege, options) options = { :user => nil, :context => nil, :user_roles => nil }.merge(options) user = options[:user] || Authorization.current_user privileges = privilege.is_a?(Array) ? privilege : [privilege] raise AuthorizationUsageError, "No user object given for user id (#{user.try(:id)}) or " + "set through Authorization.current_user" unless user roles = options[:user_roles] || flatten_roles(roles_for(user)) privileges = flatten_privileges privileges, options[:context] [user, roles, privileges] end