Spree uses CanCanCan for authorization. The permission system allows you to define granular access control for different user roles.
Permission Sets (Recommended)
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
- Roles - Determine which permission sets govern a user’s access (e.g.,
admin, customer_service)
- Permission Sets - Contain the logic defining what actions users can perform on resources
- 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 Set | Description |
|---|
SuperUser | Full admin access with safety restrictions |
DefaultCustomer | Basic storefront permissions (browse, checkout, manage own account) |
DashboardDisplay | View admin dashboard |
OrderDisplay | Read-only access to orders |
OrderManagement | Full order management (view, edit, refund, etc.) |
ProductDisplay | Read-only access to products and catalog |
ProductManagement | Full catalog management (products, variants, taxonomies) |
UserDisplay | Read-only access to users |
UserManagement | Full user management |
StockDisplay | Read-only access to inventory |
StockManagement | Full inventory management |
PromotionManagement | Manage promotions and coupon codes |
ConfigurationManagement | Manage store settings, shipping, taxes, etc. |
RoleManagement | Manage 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.