lib/pundit.rb in pundit-1.0.1 vs lib/pundit.rb in pundit-1.1.0
- old
+ new
@@ -4,14 +4,21 @@
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/module/introspection"
require "active_support/dependencies/autoload"
+# @api public
module Pundit
SUFFIX = "Policy"
+ # @api private
+ module Generators; end
+
+ # @api private
class Error < StandardError; end
+
+ # Error that will be raiser when authorization has failed
class NotAuthorizedError < Error
attr_reader :query, :record, :policy
def initialize(options = {})
if options.is_a? String
@@ -25,46 +32,90 @@
end
super(message)
end
end
+
+ # Error that will be raised if a controller action has not called the
+ # `authorize` or `skip_authorization` methods.
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.
class NotDefinedError < Error; end
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
+ # @param record [Symbol] the query method to check on the policy (e.g. `:show?`)
+ # @raise [NotAuthorizedError] if the given query method returned false
+ # @return [true] Always returns true
def authorize(user, record, query)
policy = policy!(user, record)
unless policy.public_send(query)
- raise NotAuthorizedError.new(query: query, record: record, policy: policy)
+ raise NotAuthorizedError, query: query, record: record, policy: policy
end
true
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
+ # @param record [Object] the object we're retrieving the policy scope for
+ # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
def policy_scope(user, scope)
policy_scope = PolicyFinder.new(scope).scope
policy_scope.new(user, scope).resolve if policy_scope
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
+ # @param record [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
def policy_scope!(user, scope)
PolicyFinder.new(scope).scope!.new(user, scope).resolve
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
def policy(user, record)
policy = PolicyFinder.new(record).policy
policy.new(user, record) if policy
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
def policy!(user, record)
PolicyFinder.new(record).policy!.new(user, record)
end
end
+ # @api private
module Helper
def policy_scope(scope)
pundit_policy_scope(scope)
end
end
@@ -86,71 +137,144 @@
hide_action :verify_policy_scoped
hide_action :permitted_attributes
hide_action :pundit_user
hide_action :skip_authorization
hide_action :skip_policy_scope
+ hide_action :pundit_policy_authorized?
+ hide_action :pundit_policy_scoped?
end
end
+ # @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]
def verify_authorized
- raise AuthorizationNotPerformedError unless pundit_policy_authorized?
+ raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
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
- raise PolicyScopingNotPerformedError unless pundit_policy_scoped?
+ raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
end
- def authorize(record, query=nil)
+ # 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
+ # @param record [Symbol, nil] the query method to check on the policy (e.g. `:show?`)
+ # @raise [NotAuthorizedError] if the given query method returned false
+ # @return [true] Always returns true
+ def authorize(record, query = nil)
query ||= params[:action].to_s + "?"
@_pundit_policy_authorized = true
policy = policy(record)
+
unless policy.public_send(query)
- raise NotAuthorizedError.new(query: query, record: record, policy: policy)
+ raise NotAuthorizedError, query: query, record: record, policy: policy
end
true
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]
def skip_policy_scope
@_pundit_policy_scoped = true
end
+ # Retrieves the policy scope for the given record.
+ #
+ # @see https://github.com/elabs/pundit#scopes
+ # @param record [Object] the object we're retrieving the policy scope for
+ # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
def policy_scope(scope)
@_pundit_policy_scoped = true
pundit_policy_scope(scope)
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
def policy(record)
policies[record] ||= Pundit.policy!(pundit_user, record)
end
- def permitted_attributes(record)
- name = record.class.to_s.demodulize.underscore
- params.require(name).permit(policy(record).permitted_attributes)
+ # Retrieves a set of permitted attributes from the policy by instantiating
+ # the policy class for the given record and calling `permitted_attributes` on
+ # it, or `permitted_attributes_for_{action}` if it 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
+ # @return [Hash{String => Object}] the permitted attributes
+ 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 ||= {}
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
def pundit_user
current_user
end
private