class Policy::Base

Base policy class to be extended by all other policies, authorizes users based on roles they fall into, Can be used to get the attributes or scope of roles.

@attr_reader user [Object] the user that initiated the action @attr_reader resource [Object] the object we're checking @permissions of

Attributes

resource[R]
user[R]

Public Class Methods

new(user, resource) click to toggle source
# File lib/pundit_roles/policy/base.rb, line 17
def initialize(user, resource)
  @user = user
  @resource = resource
  freeze
end

Public Instance Methods

resolve_as_association(roles, actions) click to toggle source
# File lib/pundit_roles/policy/base.rb, line 63
def resolve_as_association(roles, actions)
  permissions = self.class.permissions
  default_roles = self.class::DEFAULT_ASSOCIATED_ROLES
  associated_roles = roles.present? ? roles|default_roles : default_roles

  return unique_merge(associated_roles, permissions, actions)
end
resolve_query(query) click to toggle source

Retrieves the permitted roles for the current query, checks if user is one or more of these roles and return a hash of attributes that the user has access to.

@param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)

# File lib/pundit_roles/policy/base.rb, line 27
def resolve_query(query)
  permitted_roles = public_send(query)
  return permitted_roles if permitted_roles.is_a? TrueClass or permitted_roles.is_a? FalseClass

  validate_permission_type(permitted_roles, query)
  permissions = self.class.permissions

  if guest?
    return handle_guest_options(permitted_roles, permissions)
  end

  current_roles = determine_current_roles(permitted_roles)
  return unique_merge(current_roles, permissions)
end
resolve_scope(query) click to toggle source

Retrieves the permitted roles for the current query and checks each role, until it finds one that that the user fulfills. It returns the defined scope for that role. Scopes do no merge with other scopes

@param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)

# File lib/pundit_roles/policy/base.rb, line 46
def resolve_scope(query)
  permitted_roles = public_send(query)
  return permitted_roles if permitted_roles.is_a? TrueClass or permitted_roles.is_a? FalseClass

  validate_permission_type(permitted_roles, query)
  scopes = self.class.scopes

  if guest?
    return handle_guest_scope(permitted_roles, scopes)
  end

  current_roles =  determine_current_roles(permitted_roles)
  return false unless current_roles.present?

  return instance_eval &scopes[current_roles[0]]
end

Private Instance Methods

_allowed_permission_types() click to toggle source

@api private

# File lib/pundit_roles/policy/base.rb, line 202
def _allowed_permission_types
  [Array, FalseClass, TrueClass]
end
allow(*roles) click to toggle source

Helper method to be able to define allow: :guest, :user, etc. in the query methods

@param *roles [Array] an array of permitted roles for a particular action

# File lib/pundit_roles/policy/base.rb, line 179
def allow(*roles)
  return roles
end
determine_current_roles(permitted_roles) click to toggle source

Build an Array of the roles that the user fulfills.

@param permitted_roles [Hash] roles returned by the query

# File lib/pundit_roles/policy/base.rb, line 144
def determine_current_roles(permitted_roles)
  current_roles = []

  permitted_roles.each do |permitted_role|
    if permitted_role == :guest
      next
    end

    if test_condition?(permitted_role)
      current_roles << permitted_role
    end
  end

  return current_roles
end
guest?() click to toggle source

Default :guest role

# File lib/pundit_roles/policy/base.rb, line 184
def guest?
  @user.nil?
end
handle_guest_options(permitted_roles, permissions) click to toggle source

Return the default :guest role if guest is present in permitted_roles. Return false otherwise

@param permitted_roles [Hash] roles returned by the query @param permissions [Hash] unrefined hash of options defined by all permitted_for methods

# File lib/pundit_roles/policy/base.rb, line 77
def handle_guest_options(permitted_roles, permissions)
  if permitted_roles.include? :guest
    guest_associations = self.class.role_associations[:guest] ? self.class.role_associations[:guest] : {}
    return permissions[:guest].merge(
      {roles:
         {
           for_current_model: [:guest],
           for_associated_models: guest_associations
         }
      })
  end
  return false
end
handle_guest_scope(permitted_roles, scopes) click to toggle source
# File lib/pundit_roles/policy/base.rb, line 91
def handle_guest_scope(permitted_roles, scopes)
  if permitted_roles.include? :guest
    return instance_eval &scopes[:guest]
  end
  return false
end
merge_associated_roles(role, merged_opts) click to toggle source
# File lib/pundit_roles/policy/base.rb, line 128
def merge_associated_roles(role, merged_opts)
  associated_roles = self.class.role_associations

  return {} unless associated_roles[role].present?

  associated_roles[role].each do |k, v|
    assoc_role = {k => v}
    merged_opts = merged_opts.merge(assoc_role){ | key, old, new | old | new}
  end

  return merged_opts
end
test_condition?(role) click to toggle source

Helper method for testing the conditional of a role

@param role [Symbol] the role to be tested @raise [NoMethodError] if the test condition is undefined

# File lib/pundit_roles/policy/base.rb, line 164
def test_condition?(role)
  begin
    if send("#{role}?")
      return true
    end
  rescue NoMethodError => e
    raise NoMethodError, "Undefined test condition, it must be defined as 'role?', where, role is :#{role}, => #{e.message}"
  end

  return false
end
unique_merge(roles, permissions, requested_actions = [:show, :create, :update, :save]) click to toggle source

Uniquely merge the options of all roles that the user fulfills Returns only the action(i.e. show, create) that was requested, by default this is all actions

@param roles [Hash] roles that the user fulfills @param permissions [Hash] the options for all roles @param requested_actions [Array] the requested actions

# File lib/pundit_roles/policy/base.rb, line 104
def unique_merge(roles, permissions, requested_actions = [:show, :create, :update, :save])
  return false unless roles.present?
  merged_hash = {attributes: {}, associations: {}, roles: {for_current_model: [], for_associated_models: {}}}

  roles.each do |role|
    merged_hash[:roles][:for_current_model] |= [role]
    merged_hash[:roles][:for_associated_models] = merge_associated_roles(role, merged_hash[:roles][:for_associated_models])

    raise ArgumentError, "Role #{role} is not defined" unless permissions[role].present?

    permissions[role].each do |type, permitted_actions|
      actions = permitted_actions.slice(*requested_actions)
      actions.each do |key, value|
        unless merged_hash[type][key]
          merged_hash[type][key] = []
        end
        merged_hash[type][key] |= value
      end
    end
  end

  return merged_hash
end
validate_permission_type(permitted_roles, query) click to toggle source

@api private

# File lib/pundit_roles/policy/base.rb, line 189
def validate_permission_type(permitted_roles, query)
  valid = false
  _allowed_permission_types.each do |type|
    if permitted_roles.is_a? type
      valid = true
      break
    end
  end

  raise ArgumentError, "expected #{_allowed_permission_types} in #{query}, got #{permitted_roles.inspect}" unless valid
end