User and Roles

Spree by default comes with admin and user roles. You can create more roles in the Admin Panel UI or rails console / seed, eg.:

Spree::Role.find_or_create_by(name: 'customer_service')

Same with assigning a role, you can do it in the Admin Panel or from the console:

Spree.user_class.find_by(email: 'john@example.com').spree_roles << Spree::Role.find_or_create_by(name: 'customer_service')

Ability class

For authorization Spree uses CanCanCan library. The main ability class by default is Spree::Ability.

Adding custom permissions

Let’s assume you would like to add a new Role customer_service with some limited access to Admin Panel only Orders section.

Create a new file called 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

Please familiarize yourself with CanCanCan syntax to understand can/cannot methods more.

Now we need to inform Spree to use this ability, create another file app/models/spree/ability_decorator.rb with contents:

module Spree
  module AbilityDecorator
    def abilities_to_register
      [CustomerServiceAbility]
    end
  end

  Ability.prepend(AbilityDecorator)
end

Replacing all permissions

As we’ve mentioned earlier, Spree uses the main Ability class. You can change that to use your own custom Ability class via Dependencies in Spree initializer config/initializers/spree.rb, eg.

Spree::Dependencies.ability_class = 'CustomAbility'

Now, let’s define that new class in app/models/custom_ability.rb

class CustomAbility < Spree::Ability
  def initialize(user)
    alias_cancan_delete_action

    user ||= Spree.user_class.new

    if user.respond_to?(:has_spree_role?) && user.has_spree_role?('admin')
      apply_admin_permissions(user)
      # add some more permissions here for the admin role
    else 
      if user.respond_to?(:has_spree_role?) && user.has_spree_role?(:customer_service)
        apply_customer_service_permissions(user)
      end

      apply_user_permissions(user)
    end

    protect_admin_role
  end

  protected

  def apply_customer_service_permissions(user)
    can :manage, Spree::Order
  end
end

After restarting the rails server you observe new permissions being recognized in the application.

Further permission changes should be automatically picked up in development and should not require application restarts.