Pundit Authorization
Pundit is a simple, flexible authorization library for Ruby on Rails. This recipe shows how to integrate Pundit authorization into your Operandi.
Why Use Pundit with Services?
Centralized authorization: Keep authorization logic in policy classes
Consistent patterns: Same authorization approach across controllers and services
Testable: Policies are easy to unit test
Reusable: Services can be called from controllers, jobs, or other services with consistent authorization
Basic Setup
1. Create an Authorization Concern
# app/services/concerns/authorize_user.rb
module AuthorizeUser
extend ActiveSupport::Concern
included do
arg :current_user, type: User, optional: true, context: true
end
# Authorize an action on a record or class
def auth(record, action)
policy = policy(record)
unless policy.public_send(action)
errors.add(:authorization, "You are not authorized to perform this action")
end
end
alias_method :authorize!, :auth
# Get permitted attributes for an action
def permitted_attributes(record, action = nil)
policy = policy(record)
method_name = if action
"permitted_attributes_for_#{action}"
else
"permitted_attributes"
end
if policy.respond_to?(method_name)
attributes = policy.public_send(method_name)
params.require(param_key(record)).permit(*attributes)
else
raise Pundit::NotDefinedError, "#{method_name} not defined in #{policy.class}"
end
end
private
def policy(record)
Pundit.policy!(current_user, record)
end
def param_key(record)
if record.is_a?(Class)
record.model_name.param_key
else
record.model_name.param_key
end
end
end2. Include in ApplicationService
Usage Examples
Authorizing Actions
Authorizing on a Class (for Create actions)
Using Permitted Attributes
Policy Example
Integration with CRUD Services
Combine Pundit with the CRUD recipe for powerful, authorized services:
Handling Authorization Failures
Option 1: Collect as Errors (Default)
Option 2: Raise Exceptions
Option 3: Custom Error Handling
Testing Services with Authorization
Skipping Authorization
For system-level operations or background jobs where authorization isn't needed:
Or create a "system" flag:
What's Next?
Return to the recipes overview:
Last updated