Skip to main content

Documentation Index

Fetch the complete documentation index at: https://spreecommerce.org/docs/llms.txt

Use this file to discover all available pages before exploring further.

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: 'john@example.com')
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.