Policy Module
The Policy module provides comprehensive authorization and access control for Plutonium applications. Built on top of ActionPolicy, it offers fine-grained permissions, resource scoping, and entity-based authorization with a secure-by-default approach.
TIP
The base policy class is Plutonium::Resource::Policy. Resource policies are typically located in app/policies/.
Overview
- Resource-Level Authorization: Control access to CRUD operations and custom actions.
- Attribute-Level Permissions: Fine-grained control over which fields a user can see or edit.
- Association Control: Manage access to related resources.
- Entity Scoping: Built-in support for multi-tenant authorization.
- Secure Defaults: Policies default to denying access, requiring explicit permission.
Basic Policy Structure
Resource policies inherit from Plutonium::Resource::Policy and define methods to control access.
class PostPolicy < Plutonium::Resource::Policy
# Who can see a list of posts?
def index?
true # Everyone can see the list
end
# Who can see a single post?
def show?
record.published? || user == record.author || user.admin?
end
# Who can create a post?
def create?
user.present? # Any signed-in user
end
# Who can update a post?
def update?
user == record.author || user.admin?
end
# Who can destroy a post?
def destroy?
user.admin? # Only admins
end
# Who can run the custom 'publish' action?
def publish?
update? && record.draft? # Can only publish if you can update
end
end# Policies automatically receive context from the controller.
class Plutonium::Resource::Policy < ActionPolicy::Base
# `user` is the current authenticated user. It is required.
authorize :user, allow_nil: false
# `entity_scope` is the current portal's scoping entity (e.g., Organization).
# It is optional.
authorize :entity_scope, allow_nil: true
endSecure by Default
If a permission method (like create? or publish?) is not defined in your policy, it will default to false. You must explicitly grant permissions.
Attribute Permissions
You can control which attributes (fields) are visible or editable based on the action and user.
class PostPolicy < Plutonium::Resource::Policy
# Defines which attributes are visible on `show` and `index` pages.
def permitted_attributes_for_read
# Start with a base set of attributes
attrs = [:title, :content, :category, :published_at]
# Add admin-only attributes conditionally
attrs << :internal_notes if user.admin?
attrs
end
endclass PostPolicy < Plutonium::Resource::Policy
# Defines which attributes can be submitted in `new` and `edit` forms.
def permitted_attributes_for_create
[:title, :content, :category]
end
def permitted_attributes_for_update
# Inherits from create by default, but can be customized.
attrs = permitted_attributes_for_create
attrs << :slug if user.admin? # Only admins can edit the slug
attrs
end
endFull Permission Hierarchy
Plutonium uses a hierarchical permission system. Defining a core action permission (like read? or create?) automatically grants permission for related actions unless you override them.
Action Permissions:
index?andshow?inherit fromread?new?inherits fromcreate?edit?inherits fromupdate?update?anddestroy?inherit fromcreate?by default.
Attribute Permissions:
_for_showand_for_indexinherit from_for_read._for_newinherits from_for_create._for_editinherits from_for_update.
Scoping Collections
Use relation_scope to filter which records appear in a collection (e.g., on the index page).
class PostPolicy < Plutonium::Resource::Policy
relation_scope do |relation|
# make sure to call super unless you want to bypass the default behavior
# plutonium offers
relation = super(relation)
if user.admin?
relation # Admins see all posts
else
# Other users only see their own posts or published posts
relation.where(author: user).or(relation.where(published: true))
end
end
endclass PostPolicy < Plutonium::Resource::Policy
relation_scope do |relation|
# `super` applies the portal's entity scoping first.
# e.g., `relation.associated_with(current_organization)`
relation = super(relation)
# Then, apply additional logic.
if user.admin?
relation
else
relation.where(published: true)
end
end
endAssociation Permissions
Control which associated resources can be accessed or rendered.
class PostPolicy < Plutonium::Resource::Policy
# This determines which associations can be rendered in the UI,
# especially for nested forms or displays.
def permitted_associations
[:comments, :author, :tags]
end
end