Skip to main content
Spree uses CanCanCan for authorization. The permission system allows you to define granular access control for different user roles. Permission Sets provide a clean, modular way to manage permissions. Each permission set is a reusable group of permissions that can be assigned to roles.

How It Works

  1. Roles - Determine which permission sets govern a user’s access (e.g., admin, customer_service)
  2. Permission Sets - Contain the logic defining what actions users can perform on resources
  3. Role Configuration - Associates roles with their corresponding permission sets

Configuring Roles

In your config/initializers/spree.rb, configure which permission sets are assigned to each role:
Rails.application.config.after_initialize do
  # Default permissions for all users (guests and logged-in customers)
  Spree.permissions.assign(:default, [Spree::PermissionSets::DefaultCustomer])

  # Full admin access
  Spree.permissions.assign(:admin, [Spree::PermissionSets::SuperUser])

  # Custom role with specific permissions
  Spree.permissions.assign(:customer_service, [
    Spree::PermissionSets::DashboardDisplay,
    Spree::PermissionSets::OrderManagement,
    Spree::PermissionSets::UserDisplay
  ])

  # Merchandiser role for product management
  Spree.permissions.assign(:merchandiser, [
    Spree::PermissionSets::DashboardDisplay,
    Spree::PermissionSets::ProductManagement,
    Spree::PermissionSets::StockManagement
  ])
end

Built-in Permission Sets

Permission SetDescription
SuperUserFull admin access with safety restrictions
DefaultCustomerBasic storefront permissions (browse, checkout, manage own account)
DashboardDisplayView admin dashboard
OrderDisplayRead-only access to orders
OrderManagementFull order management (view, edit, refund, etc.)
ProductDisplayRead-only access to products and catalog
ProductManagementFull catalog management (products, variants, taxonomies)
UserDisplayRead-only access to users
UserManagementFull user management
StockDisplayRead-only access to inventory
StockManagementFull inventory management
PromotionManagementManage promotions and coupon codes
ConfigurationManagementManage store settings, shipping, taxes, etc.
RoleManagementManage roles (except the protected admin role)

Creating Custom Permission Sets

Create a new permission set in app/models/spree/permission_sets/:
# app/models/spree/permission_sets/warehouse_management.rb
module Spree
  module PermissionSets
    class WarehouseManagement < Base
      def activate!
        can :manage, Spree::StockItem
        can :manage, Spree::StockLocation
        can :manage, Spree::StockMovement
        can :manage, Spree::Shipment
        can [:read, :admin], Spree::Order
      end
    end
  end
end
Then assign it to a role:
Spree.permissions.assign(:warehouse_staff, [
  Spree::PermissionSets::DashboardDisplay,
  Spree::PermissionSets::WarehouseManagement
])

Permission Set API

Within a permission set, you have access to:
  • can(action, subject, conditions = {}) - Grant permission
  • cannot(action, subject, conditions = {}) - Deny permission
  • can?(action, subject) - Check if permission exists
  • user - The current user
  • store - The current store (for multi-store setups)
module Spree
  module PermissionSets
    class OrderManagementForOwnOrders < Base
      def activate!
        # Users can only manage orders they created
        can :manage, Spree::Order, created_by_id: user.id

        # But cannot cancel any order
        cannot :cancel, Spree::Order

        # Unless it's cancellable
        can :cancel, Spree::Order, &:allow_cancel?
      end
    end
  end
end

Managing Permission Configuration

# Assign permission sets to a role
Spree.permissions.assign(:customer_service, [
  Spree::PermissionSets::OrderDisplay,
  Spree::PermissionSets::UserManagement
])

# Add more permission sets to an existing role
Spree.permissions.assign(:customer_service, [
  Spree::PermissionSets::StockDisplay
])

# Clear all permission sets from a role
Spree.permissions.clear(:customer_service)

# Check what permission sets a role has
Spree.permissions.permission_sets_for(:customer_service)
# => [Spree::PermissionSets::OrderDisplay, ...]

# Check if a role is configured
Spree.permissions.role_configured?(:customer_service)
# => true

Users and Roles

Spree comes with an admin role by default. You can create more roles in the Admin Panel or via Rails console:
Spree::Role.find_or_create_by(name: 'customer_service')
Spree::Role.find_or_create_by(name: 'merchandiser')
Spree::Role.find_or_create_by(name: 'warehouse_staff')
Assign a role to a user:
user = Spree.user_class.find_by(email: '[email protected]')
role = Spree::Role.find_by(name: 'customer_service')
user.spree_roles << role

Default Role Behavior

  • Users with no roles assigned automatically get the :default role permissions
  • No RoleUser records are created for regular customers
  • Only users with special permissions need explicit role assignments

Legacy Approach: Custom Ability Classes

The permission sets approach above is recommended for new projects. The legacy approach below is supported for backward compatibility.

Adding Custom Permissions via Decorator

Create a new ability class in app/models/customer_service_ability.rb:
class CustomerServiceAbility
  include CanCan::Ability

  def initialize(user)
    if user.respond_to?(:has_spree_role?) && user.has_spree_role?('customer_service')
      can :manage, Spree::Order
    end
  end
end
Register it via a decorator in app/models/spree/ability_decorator.rb:
module Spree
  module AbilityDecorator
    def abilities_to_register
      [CustomerServiceAbility]
    end
  end

  Ability.prepend(AbilityDecorator)
end

Replacing the Ability Class

You can replace the entire ability class via Dependencies:
# config/initializers/spree.rb
Spree::Dependencies.ability_class = 'CustomAbility'
# app/models/custom_ability.rb
class CustomAbility < Spree::Ability
  def initialize(user, options = {})
    alias_cancan_delete_action

    @user = user || Spree.user_class.new
    @store = options[:store] || Spree::Current.store

    if @user.respond_to?(:has_spree_role?) && @user.has_spree_role?('admin')
      apply_admin_permissions(@user, options)
    elsif @user.respond_to?(:has_spree_role?) && @user.has_spree_role?(:customer_service)
      apply_customer_service_permissions(@user)
    else
      apply_user_permissions(@user, options)
    end

    protect_admin_role
  end

  protected

  def apply_customer_service_permissions(user)
    can :manage, Spree::Order
    can [:read, :admin], Spree.user_class
  end
end

CanCanCan Reference

Spree’s permission system is built on CanCanCan. Key concepts:
  • can :action, Subject - Grant permission
  • can :manage, Subject - Grant all actions (create, read, update, destroy)
  • cannot :action, Subject - Explicitly deny permission
  • can :action, Subject, conditions - Conditional permission
# Examples
can :read, Spree::Product                           # Can read any product
can :manage, Spree::Order, user_id: user.id        # Can manage own orders
can :update, Spree::Order do |order|               # Block conditions
  order.user == user && !order.completed?
end
cannot :destroy, Spree::Order                       # Cannot destroy any order
can :destroy, Spree::Order, &:can_be_deleted?      # Unless it's deletable
See the CanCanCan documentation for more details.