1
0
Fork 0
mirror of https://github.com/varvet/pundit.git synced 2022-11-09 12:30:11 -05:00
varvet--pundit/lib/pundit.rb

278 lines
9.6 KiB
Ruby
Raw Normal View History

2016-01-16 22:06:44 -05:00
# frozen_string_literal: true
2012-11-04 04:20:45 -05:00
require "pundit/version"
2012-11-19 04:57:17 -05:00
require "pundit/policy_finder"
2012-11-19 07:02:42 -05:00
require "active_support/concern"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/module/introspection"
2014-04-23 00:02:03 -04:00
require "active_support/dependencies/autoload"
2012-11-04 04:20:45 -05:00
# @api public
2012-11-04 04:20:45 -05:00
module Pundit
2016-01-16 22:06:44 -05:00
SUFFIX = "Policy".freeze
# @api private
module Generators; end
# @api private
2015-04-07 14:46:50 -04:00
class Error < StandardError; end
# Error that will be raised when authorization has failed
2015-04-07 14:46:50 -04:00
class NotAuthorizedError < Error
attr_reader :query, :record, :policy
def initialize(options = {})
if options.is_a? String
message = options
else
@query = options[:query]
@record = options[:record]
@policy = options[:policy]
message = options.fetch(:message) { "not allowed to #{query} this #{record.inspect}" }
end
super(message)
end
end
# Error that will be raised if a controller action has not called the
# `authorize` or `skip_authorization` methods.
2015-04-07 14:46:50 -04:00
class AuthorizationNotPerformedError < Error; end
# Error that will be raised if a controller action has not called the
# `policy_scope` or `skip_policy_scope` methods.
class PolicyScopingNotPerformedError < AuthorizationNotPerformedError; end
# Error that will be raised if a policy or policy scope is not defined.
2015-04-07 14:46:50 -04:00
class NotDefinedError < Error; end
2012-11-19 04:57:17 -05:00
extend ActiveSupport::Concern
class << self
# Retrieves the policy for the given record, initializing it with the
# record and user and finally throwing an error if the user is not
# authorized to perform the given action.
#
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're checking permissions of
2016-02-24 08:05:38 -05:00
# @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
def authorize(user, record, query)
policy = policy!(user, record)
unless policy.public_send(query)
2016-01-14 09:15:30 -05:00
raise NotAuthorizedError, query: query, record: record, policy: policy
end
record
end
# Retrieves the policy scope for the given record.
#
# @see https://github.com/elabs/pundit#scopes
# @param user [Object] the user that initiated the action
2016-02-24 08:05:38 -05:00
# @param scope [Object] the object we're retrieving the policy scope for
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
2014-08-22 05:19:36 -04:00
def policy_scope(user, scope)
policy_scope = PolicyFinder.new(scope).scope
2013-11-05 12:26:38 -05:00
policy_scope.new(user, scope).resolve if policy_scope
2012-11-19 04:57:17 -05:00
end
# Retrieves the policy scope for the given record.
#
# @see https://github.com/elabs/pundit#scopes
# @param user [Object] the user that initiated the action
2016-02-24 08:05:38 -05:00
# @param scope [Object] the object we're retrieving the policy scope for
# @raise [NotDefinedError] if the policy scope cannot be found
# @return [Scope{#resolve}] instance of scope class which can resolve to a scope
2014-08-22 05:19:36 -04:00
def policy_scope!(user, scope)
PolicyFinder.new(scope).scope!.new(user, scope).resolve
2012-11-19 04:57:17 -05:00
end
# Retrieves the policy for the given record.
#
# @see https://github.com/elabs/pundit#policies
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're retrieving the policy for
# @return [Object, nil] instance of policy class with query methods
2014-08-22 05:19:36 -04:00
def policy(user, record)
policy = PolicyFinder.new(record).policy
2013-11-05 12:26:38 -05:00
policy.new(user, record) if policy
2012-11-19 04:57:17 -05:00
end
# Retrieves the policy for the given record.
#
# @see https://github.com/elabs/pundit#policies
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're retrieving the policy for
# @raise [NotDefinedError] if the policy cannot be found
# @return [Object] instance of policy class with query methods
2014-08-22 05:19:36 -04:00
def policy!(user, record)
PolicyFinder.new(record).policy!.new(user, record)
2012-11-19 04:57:17 -05:00
end
end
# @api private
module Helper
def policy_scope(scope)
pundit_policy_scope(scope)
end
end
2012-11-19 04:57:17 -05:00
included do
helper Helper if respond_to?(:helper)
2012-11-19 04:57:17 -05:00
if respond_to?(:helper_method)
helper_method :policy
helper_method :pundit_policy_scope
helper_method :pundit_user
2012-11-19 04:57:17 -05:00
end
end
protected
# @return [Boolean] whether authorization has been performed, i.e. whether
# one {#authorize} or {#skip_authorization} has been called
def pundit_policy_authorized?
!!@_pundit_policy_authorized
end
# @return [Boolean] whether policy scoping has been performed, i.e. whether
# one {#policy_scope} or {#skip_policy_scope} has been called
def pundit_policy_scoped?
!!@_pundit_policy_scoped
end
# Raises an error if authorization has not been performed, usually used as an
# `after_action` filter to prevent programmer error in forgetting to call
# {#authorize} or {#skip_authorization}.
#
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
# @raise [AuthorizationNotPerformedError] if authorization has not been performed
# @return [void]
2012-11-19 04:57:17 -05:00
def verify_authorized
2016-01-14 09:15:30 -05:00
raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
2012-11-19 04:57:17 -05:00
end
# Raises an error if policy scoping has not been performed, usually used as an
# `after_action` filter to prevent programmer error in forgetting to call
# {#policy_scope} or {#skip_policy_scope} in index actions.
#
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
# @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
# @return [void]
def verify_policy_scoped
2016-01-14 09:15:30 -05:00
raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
end
# Retrieves the policy for the given record, initializing it with the record
# and current user and finally throwing an error if the user is not
# authorized to perform the given action.
#
# @param record [Object] the object we're checking permissions of
2016-02-24 08:05:38 -05:00
# @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
# If omitted then this defaults to the Rails controller action name.
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
2016-01-14 09:15:30 -05:00
def authorize(record, query = nil)
2012-11-19 04:57:17 -05:00
query ||= params[:action].to_s + "?"
@_pundit_policy_authorized = true
policy = policy(record)
2016-01-14 09:15:30 -05:00
unless policy.public_send(query)
2016-01-14 09:15:30 -05:00
raise NotAuthorizedError, query: query, record: record, policy: policy
2012-11-19 04:57:17 -05:00
end
record
2012-11-19 04:57:17 -05:00
end
# Allow this action not to perform authorization.
#
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
# @return [void]
def skip_authorization
@_pundit_policy_authorized = true
end
# Allow this action not to perform policy scoping.
#
# @see https://github.com/elabs/pundit#ensuring-policies-are-used
# @return [void]
2015-04-07 21:17:32 -04:00
def skip_policy_scope
@_pundit_policy_scoped = true
end
# Retrieves the policy scope for the given record.
#
# @see https://github.com/elabs/pundit#scopes
2016-02-24 08:05:38 -05:00
# @param scope [Object] the object we're retrieving the policy scope for
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
2012-11-19 04:57:17 -05:00
def policy_scope(scope)
@_pundit_policy_scoped = true
pundit_policy_scope(scope)
2012-11-19 04:57:17 -05:00
end
# Retrieves the policy for the given record.
#
# @see https://github.com/elabs/pundit#policies
# @param record [Object] the object we're retrieving the policy for
# @return [Object, nil] instance of policy class with query methods
2012-11-19 04:57:17 -05:00
def policy(record)
policies[record] ||= Pundit.policy!(pundit_user, record)
end
# Retrieves a set of permitted attributes from the policy by instantiating
# the policy class for the given record and calling `permitted_attributes` on
2016-02-24 08:05:38 -05:00
# it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
# what key the record should have in the params hash and retrieves the
# permitted attributes from the params hash under that key.
#
# @see https://github.com/elabs/pundit#strong-parameters
# @param record [Object] the object we're retrieving permitted attributes for
2016-02-24 08:05:38 -05:00
# @param action [Symbol, String] the name of the action being performed on the record (e.g. `:update`).
# If omitted then this defaults to the Rails controller action name.
# @return [Hash{String => Object}] the permitted attributes
2016-01-14 09:15:30 -05:00
def permitted_attributes(record, action = params[:action])
param_key = PolicyFinder.new(record).param_key
policy = policy(record)
method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
"permitted_attributes_for_#{action}"
else
"permitted_attributes"
end
params.require(param_key).permit(*policy.public_send(method_name))
end
# Cache of policies. You should not rely on this method.
#
# @api private
def policies
@_pundit_policies ||= {}
end
# Cache of policy scope. You should not rely on this method.
#
# @api private
def policy_scopes
@_pundit_policy_scopes ||= {}
2013-07-12 23:42:34 -04:00
end
# Hook method which allows customizing which user is passed to policies and
# scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
#
# @see https://github.com/elabs/pundit#customize-pundit-user
# @return [Object] the user object to be used with pundit
2013-07-12 23:42:34 -04:00
def pundit_user
current_user
2012-11-19 04:57:17 -05:00
end
private
def pundit_policy_scope(scope)
policy_scopes[scope] ||= Pundit.policy_scope!(pundit_user, scope)
end
2012-11-04 04:20:45 -05:00
end