class Accessly::Policy::Base

Constants

ACTIONS_MODULE

Module that will hold our meta-programmed action methods just above the policy class in the inheritance hierarchy; allowing for them to be overridden in the policy.

Attributes

actor[R]

Public Class Methods

actions(actions) click to toggle source

Meta-programs action methods from actions supplied. Used in policies as a DSL to declare actions.

This defines the actions on the `actions_module` so that they are positioned higher in the inheritance tree than methods defined on the class itself. This will allow us to define methods that override these base methods and call `super`.

@param actions [Hash] the actions to define on the policy

@example Define Actions

# This example causes the following methods to be defined:
# some_action? : Returns true if the actor has the some_action
#   permission, false otherwise
# flip_the_flop? : Returns true if the actor has the flip_the_flop
#   permission, false otherwise
# create? : Returns true if the actor has the create permission, false
#   otherwise
actions(
  some_action: 1,
  flip_the_flop: 2,
  create: 3
)

@return [Hash] actions

# File lib/accessly/policy/base.rb, line 41
def self.actions(actions)
  _actions.merge!(actions)
  actions.each do |action, action_id|
    actions_module.module_eval do
      define_method(:"#{action}?") do |*args|
        _can_do_action?(action, action_id, args.first)
      end
    end
  end
end
actions_on_objects(actions_on_objects) click to toggle source

Meta-programs action_on_objects methods from the actions supplied. Used in policies as a DSL to declare actions on objects. It is different from actions in that it will also define a method for listing all objects authorized with this action for the given actor and that these actions will always be associated not only with an actor, but with an object of the action.

@param actions_on_objects [Hash] the actions on objects to define on the policy

@example Define Actions On Objects

# This example causes the following methods to be defined:
# edit : Returns an ActiveRecord::Relation of the objects on which
#   the actor has the edit permission
# edit?(object) : Returns true if the actor has the edit permission
#   on the given object, false otherwise
# show : Returns an ActiveRecord::Relation of the objects on which
#   the actor has the show permission
# show?(object) : Returns true if the actor has the show permission
#   on the given object, false otherwise
actions_on_objects(
  edit: 1,
  show: 2
)

@return [Hash] actions_on_objects

# File lib/accessly/policy/base.rb, line 79
def self.actions_on_objects(actions_on_objects)
  _actions_on_objects.merge!(actions_on_objects)
  actions_on_objects.each do |action, action_id|
    actions_module.module_eval do
      define_method(:"#{action}?") do |*args|
        _can_do_action?(action, action_id, args.first)
      end

      define_method(action) do |*args|
        _list_for_action(action, action_id)
      end
    end
  end
end
model_scope() click to toggle source
# File lib/accessly/policy/base.rb, line 102
def self.model_scope
  raise ArgumentError.new("#model_scope is not defined on #{self.name}.")
end
namespace() click to toggle source
# File lib/accessly/policy/base.rb, line 94
def self.namespace
  String(self)
end
new(actor) click to toggle source
# File lib/accessly/policy/base.rb, line 11
def initialize(actor)
  @actor = actor
end

Private Class Methods

_action_defined?(action_name) click to toggle source
# File lib/accessly/policy/base.rb, line 275
def self._action_defined?(action_name)
  _actions.include?(action_name) || _actions_on_objects.include?(action_name)
end
_actions() click to toggle source
# File lib/accessly/policy/base.rb, line 283
def self._actions
  @_actions ||= {}
end
_actions_on_objects() click to toggle source
# File lib/accessly/policy/base.rb, line 291
def self._actions_on_objects
  @_actions_on_objects ||= {}
end
actions_module() click to toggle source

Accessor for the actions module that will hold our meta-programmed methods. We put these methods in this module so that they are positioned above the policy in the inheritance chain. We can then override the methods in our policy as needed and call super to access the previous definition.

@return [ACTIONS_MODULE] the module for holding actions currently

defined on this class.
# File lib/accessly/policy/base.rb, line 183
def self.actions_module
  if const_defined?(ACTIONS_MODULE, _search_ancestors = false)
    mod = const_get(ACTIONS_MODULE)
  else
    mod = const_set(ACTIONS_MODULE, Module.new)
    include mod
  end

  mod
end

Public Instance Methods

accessly_query() click to toggle source
# File lib/accessly/policy/base.rb, line 150
def accessly_query
  @_accessly_query ||= begin
    query = Accessly::Query.new(actors)
    query.on_segment(segment_id) unless segment_id.nil?
    query
  end
end
actors() click to toggle source

Specifies all the actors used in permission lookups. Override this method in child policy classes to specify other actors that the actor given in the initializer may inherit permissions from.

# File lib/accessly/policy/base.rb, line 114
def actors
  actor
end
can?(action, object = nil) click to toggle source
# File lib/accessly/policy/base.rb, line 126
def can?(action, object = nil)
  if object.nil?
    send("#{action}?")
  else
    send("#{action}?", object)
  end
end
grant!(action, object = nil) click to toggle source
# File lib/accessly/policy/base.rb, line 138
def grant!(action, object = nil)
  object_id = _get_object_id(object)
  action_id = _get_action_id(action, object_id)
  grant_object.grant!(action_id, namespace, object_id)
end
grant_object() click to toggle source
# File lib/accessly/policy/base.rb, line 158
def grant_object
  grant_object = Accessly::Permission::Grant.new(actor)
  grant_object.on_segment(segment_id) unless segment_id.nil?

  grant_object
end
list(action) click to toggle source
# File lib/accessly/policy/base.rb, line 134
def list(action)
  send(action)
end
model_scope() click to toggle source
# File lib/accessly/policy/base.rb, line 106
def model_scope
  self.class.model_scope
end
namespace() click to toggle source
# File lib/accessly/policy/base.rb, line 98
def namespace
  self.class.namespace
end
revoke!(action, object = nil) click to toggle source
# File lib/accessly/policy/base.rb, line 144
def revoke!(action, object = nil)
  object_id = _get_object_id(object)
  action_id = _get_action_id(action, object_id)
  revoke_object.revoke!(action_id, namespace, object_id)
end
revoke_object() click to toggle source
# File lib/accessly/policy/base.rb, line 165
def revoke_object
  revoke_object = Accessly::Permission::Revoke.new(actor)
  revoke_object.on_segment(segment_id) unless segment_id.nil?

  revoke_object
end
segment_id() click to toggle source
# File lib/accessly/policy/base.rb, line 122
def segment_id
  nil
end
unrestricted?() click to toggle source
# File lib/accessly/policy/base.rb, line 118
def unrestricted?
  false
end

Private Instance Methods

_action_defined?(action_name) click to toggle source
# File lib/accessly/policy/base.rb, line 279
def _action_defined?(action_name)
  self.class._action_defined?(action_name)
end
_actions() click to toggle source
# File lib/accessly/policy/base.rb, line 287
def _actions
  self.class._actions
end
_actions_on_objects() click to toggle source
# File lib/accessly/policy/base.rb, line 295
def _actions_on_objects
  self.class._actions_on_objects
end
_can_do_action?(action, action_id, object) click to toggle source

Determines whether the caller is calling an object action method or a non-object action method and calls the appropriate implementation.

# File lib/accessly/policy/base.rb, line 205
def _can_do_action?(action, action_id, object)
  if object.nil?
    _can_do_action_without_object?(action, action_id)
  else
    _can_do_action_with_object?(action, action_id, object)
  end
end
_can_do_action_with_object?(action, action_id, object) click to toggle source

Determines whether the actor has permission to do the action on an object. If the actor should have unrestricted access, then this returns true without checking.

@return [Boolean]

# File lib/accessly/policy/base.rb, line 233
def _can_do_action_with_object?(action, action_id, object)
  object_id = _get_object_id(object)

  if _actions_on_objects[action].nil?
    _invalid_action_on_object!(action)
  elsif unrestricted?
    true
  else
    accessly_query.can?(action_id, namespace, object_id)
  end
end
_can_do_action_without_object?(action, action_id) click to toggle source

Determines whether the actor has permission to do the action outside of an object context. If the actor should have unrestricted access, then this returns true without checking.

@return [Boolean]

# File lib/accessly/policy/base.rb, line 218
def _can_do_action_without_object?(action, action_id)
  if _actions[action].nil?
    _invalid_general_action!(action)
  elsif unrestricted?
    true
  else
    accessly_query.can?(action_id, namespace)
  end
end
_get_action_id(action, object_id = nil) click to toggle source
# File lib/accessly/policy/base.rb, line 194
def _get_action_id(action, object_id = nil)
  if object_id.nil?
    _get_general_action_id!(action)
  else
    _get_action_on_object_id!(action)
  end
end
_get_action_on_object_id!(action) click to toggle source
# File lib/accessly/policy/base.rb, line 259
def _get_action_on_object_id!(action)
  _actions_on_objects[action] || _invalid_action_on_object!(action)
end
_get_general_action_id!(action) click to toggle source
# File lib/accessly/policy/base.rb, line 255
def _get_general_action_id!(action)
  _actions[action] || _invalid_general_action!(action)
end
_get_object_id(object) click to toggle source
# File lib/accessly/policy/base.rb, line 271
def _get_object_id(object)
  object.respond_to?(:id) ? object.id : object
end
_invalid_action_on_object!(action) click to toggle source
# File lib/accessly/policy/base.rb, line 267
def _invalid_action_on_object!(action)
  raise ArgumentError.new("#{action} is not defined as an action-on-object for #{self.class.name}")
end
_invalid_general_action!(action) click to toggle source
# File lib/accessly/policy/base.rb, line 263
def _invalid_general_action!(action)
  raise ArgumentError.new("#{action} is not defined as a general action for #{self.class.name}")
end
_list_for_action(action, action_id) click to toggle source
# File lib/accessly/policy/base.rb, line 245
def _list_for_action(action, action_id)
  if _actions_on_objects[action].nil?
    _invalid_action_on_object!(action)
  elsif unrestricted?
    model_scope
  else
    model_scope.where(id: accessly_query.list(action_id, namespace))
  end
end