Agent skill

policy

Plutonium resource policies - authorization, attribute permissions, and scoping

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/policy

SKILL.md

Plutonium Policies

Policies control WHO can do WHAT with resources. Built on ActionPolicy.

Plutonium extends ActionPolicy with:

  • Attribute permissions (permitted_attributes_for_*)
  • Association permissions (permitted_associations)
  • Automatic entity scoping for multi-tenancy
  • Derived action methods (e.g., update? inherits from create?)

Base Class

ruby
# app/policies/resource_policy.rb (generated during install)
class ResourcePolicy < Plutonium::Resource::Policy
  # App-wide authorization defaults
end

# app/policies/post_policy.rb (per resource)
class PostPolicy < ResourcePolicy
  def create?
    user.present?
  end

  def read?
    true
  end

  def permitted_attributes_for_create
    %i[title content]
  end

  def permitted_attributes_for_read
    %i[title content author created_at]
  end
end

Action Permissions

Core Actions (Must Override)

ruby
def create?  # Default: false - MUST override
  user.present?
end

def read?    # Default: false - MUST override
  true
end

Derived Actions (Inherit by Default)

Method Inherits From Override When
update? create? Different update rules
destroy? create? Different delete rules
index? read? Custom listing rules
show? read? Record-specific read rules
new? create? Rarely needed
edit? update? Rarely needed
search? index? Search-specific rules

Custom Actions

Define methods matching your action names:

ruby
def publish?
  update? && record.draft?
end

def archive?
  create? && !record.archived?
end

def invite_user?
  user.admin?
end

Actions are secure by default - undefined methods return false.

Attribute Permissions

Core Methods (Must Override for Production)

ruby
# What users can see (index, show)
def permitted_attributes_for_read
  %i[title content author published_at created_at]
end

# What users can set (create, update)
def permitted_attributes_for_create
  %i[title content]
end

Derived Methods (Inherit by Default)

Method Inherits From
permitted_attributes_for_update permitted_attributes_for_create
permitted_attributes_for_index permitted_attributes_for_read
permitted_attributes_for_show permitted_attributes_for_read
permitted_attributes_for_new permitted_attributes_for_create
permitted_attributes_for_edit permitted_attributes_for_update

Per-Action Attributes

Show different fields for different views:

ruby
def permitted_attributes_for_index
  %i[title author created_at]  # Minimal for list
end

def permitted_attributes_for_read
  %i[title content author tags created_at updated_at]  # Full for detail
end

Auto-Detection (Development Only)

In development, undefined attribute methods auto-detect from the model. This raises errors in production - always define explicitly.

Association Permissions

Control which associations can be rendered:

ruby
def permitted_associations
  %i[comments tags author]
end

Used for:

  • Nested forms
  • Related data displays
  • Association fields in tables

Collection Scoping

Filter which records users can see:

ruby
relation_scope do |relation|
  if user.admin?
    relation
  else
    relation.where(author: user)
  end
end

With Entity Scoping

Call super to preserve automatic entity scoping:

ruby
relation_scope do |relation|
  relation = super(relation)  # Apply entity scope first

  if user.admin?
    relation
  else
    relation.where(published: true)
  end
end

Portal-Specific Policies

Override policies per portal:

ruby
# Base policy
class PostPolicy < ResourcePolicy
  def create?
    user.present?
  end
end

# Admin portal - more permissive
class AdminPortal::PostPolicy < ::PostPolicy
  include AdminPortal::ResourcePolicy

  def destroy?
    true  # Admins can always delete
  end

  def permitted_attributes_for_create
    %i[title content featured internal_notes]  # More fields
  end
end

# Public portal - restricted
class PublicPortal::PostPolicy < ::PostPolicy
  include PublicPortal::ResourcePolicy

  def create?
    false  # No public creation
  end
end

Common Patterns

Check Model Capabilities

ruby
def archive?
  return false unless record.respond_to?(:archived!)
  return false if record.archived?

  user.admin?
end

Prevent Actions on Archived Records

ruby
def update?
  return false if record.try(:archived?)
  super
end

def destroy?
  return false if record.try(:archived?)
  super
end

Owner-Based Permissions

ruby
def update?
  record.author == user || user.admin?
end

def destroy?
  update?  # Same rules as update
end

Role-Based Permissions

ruby
def create?
  user.admin? || user.editor?
end

def read?
  true  # Everyone can read
end

def update?
  return true if user.admin?
  return true if user.editor? && record.author == user
  false
end

Conditional Attribute Access

ruby
def permitted_attributes_for_create
  attrs = %i[title content]
  attrs << :featured if user.admin?
  attrs << :author_id if user.admin?  # Only admins can set author
  attrs
end

Authorization Context

Policies have access to:

ruby
user         # Current user (required)
record       # The resource being authorized
entity_scope # Current scoped entity (for multi-tenancy)

Custom Context

Add custom context in controllers:

ruby
# In policy
class PostPolicy < ResourcePolicy
  authorize :department, allow_nil: true

  def create?
    department&.allows_posting?
  end
end

# In controller
class PostsController < ResourceController
  authorize :department, through: :current_department

  private

  def current_department
    current_user.department
  end
end

Controller Integration

Built-in CRUD actions automatically:

  • Call authorize_current! at the start of each action
  • Apply relation_scope for index/listings
  • Filter params through permitted_attributes

After-action callbacks verify authorization was performed - if you add custom actions, you must call authorize_current! yourself or skip verification.

Skip Verification (When Needed)

ruby
class PostsController < ResourceController
  skip_verify_authorize_current only: [:custom_action]

  def custom_action
    # Handle authorization manually
  end
end

Best Practices

  1. Always override create? and read? - They default to false
  2. Define attributes explicitly - Auto-detection only works in development
  3. Call super in relation_scope - Preserves entity scoping
  4. Use derived methods - Let update? inherit from create? when appropriate
  5. Keep policies focused - Authorization logic only, no business logic
  6. Test edge cases - Archived records, nil associations, role combinations

Related Skills

  • resource - How policies fit in the resource architecture
  • definition-actions - Actions that need policy methods
  • controller - How controllers use policies

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results