Action Policy Behaviour
Action Policy provides a mixin called ActionPolicy::Behaviour which adds authorization methods to your classes.
Usage
Let's make our custom service object aware of authorization:
class PostUpdateAction
# First, we should include the behaviour
include ActionPolicy::Behaviour
# Secondly, provide authorization subject (performer)
authorize :user
attr_reader :user
def initialize(user)
@user = user
end
def call(post, params)
# Now we can use authorization methods
authorize! post, to: :update?
post.update!(params)
end
endActionPolicy::Behaviour provides authorize class-level method to configure authorization context and the instance-level methods: authorize!, allowed_to?, allowance_to, and authorized:
authorize!
This is a guard-method which raises an ActionPolicy::Unauthorized exception if authorization failed (i.e. policy rule returns false):
# `to` is a name of the policy rule to apply
authorize! post, to: :update?allowed_to?
This is a predicate version of authorize!: it returns true if authorization succeed and false otherwise:
# the first argument is the rule to apply
# the second one is the target
if allowed_to?(:edit?, post)
# ...
endallowance_to
This method is similar to allowed_to? but returns an authorization result instead. It's especially useful for APIs when you want to return not only true or false but also, for example, failure reasons:
result = allowance_to(:edit?, post)
{value: result.value, fullMessages: result.reasons.full_messages, details: result.reasons.details}.to_jsonauthorized
See scoping docs.
Policy lookup
All three instance methods (authorize!, allowed_to?, authorized) uses the same policy_for to lookup a policy class for authorization target. So, you can provide additional options to control the policy lookup process:
- Explicitly specify policy class using
withoption:
allowed_to?(:edit?, post, with: SpecialPostPolicy)- Provide a namespace:
# Would try to lookup Admin::PostPolicy first
authorize! post, to: :destroy?, namespace: Admin- Provide a strict_namespace lookup option:
# Would not fallback lookup PostPolicy if Admin::PostPolicy doesn't exist
authorize! post, to: :destroy?, namespace: Admin, strict_namespace: true
# or by overriding a specific behavior method
def authorization_strict_namespace
true
end- Define a default policy to use in case lookup finds nothing:
# either explicitly
authorize! post, to: :destroy?, default: GuestPolicy
# or by overriding a specific behavior method
def default_authorization_policy_class
logged_in? ? DefaultUserPolicy : GuestPolicy
endImplicit authorization target
You can omit the authorization target for all the methods by defining an implicit authorization target:
class PostActions
include ActionPolicy::Behaviour
authorize :user
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def update(params)
# post is used here implicitly as a target
authorize! to: :update
post.update!(params)
end
def destroy
# post is used here implicitly as a target
authorize! to: :destroy
post.destroy!
end
def implicit_authorization_target
post
end
end