Your eCommerce Needs Authorization: Enter Solidus

Georgy Shabunin

24 Apr 2019 Development, Solidus, eCommerce

Georgy Shabunin

4 mins
Your eCommerce Needs Authorization: Enter Solidus

Ensuring privacy of customer data is undoubtedly a matter of great importance for any reputable retail business going online. Setting up an online presence entails global access to business resources including goods, services and personal information collected during interaction with customers. It is, therefore, necessary that adequate mechanisms are put in place to guarantee confidentiality and integrity of business data made available online.

Access control is a primary security concern. Generally, there are two main ingredients to the access control equation:

  • Authentication. To grant access, the system must first establish the identity of the subject initiating the request to the server. The identification process is called authentication and can be performed by either a human or by yet another system;
  • Authorization. With the obtained identity the system can proceed to reason about the actions available to the current user within the system. Restricting access to resources based on the given identity, or the absence of one (e.g. guest user), is called authorization.

Both authentication and authorization are off the shelf features of Solidus. This article delves into authorization and how it can be set up within Solidus to align with your business needs.

Built on CanCan(Can)

Authorization in Solidus is built upon the CanCan(Can) gem, which offers an expressive DSL to define and verify user permissions against application resources. CanCan allows one to avoid access control logic being scattered throughout the application source code. Instead, it enables one to define all user permissions in Ability classes. For instance, here is a simplified Ability class for an eCommerce application:

class Ability
  include CanCan::Ability
  def initialize(user)
    user ||= User.new
    if user.admin?
      can :manage, :all
    else
      can :display, Product
      can :display, Taxon
      can :display, Taxonomy
      can :display, Variant
    end
  end
end

With the growth of the application, ability files like the one above tend to quickly become massive and hence hard to maintain. With that in mind, Solidus augments CanCan interface to facilitate implementation and maintenance of complex ability specifications.

Let's go through the steps required to set up a custom permission system within Solidus by examining the configuration of those permissions that already ship with the eCommerce platform.

Roles

Solidus comes with two preconfigured roles: admin and default. User that has_spree_role?(:admin) has access to the admin panel and can manage all resources. User that has_spree_role?(:default) resembles a client or a website visitor, that can view certain resources, manage their shopping carts, etc. Here are two main options to create a new Spree::Role:

  • Manually add a row in the spree_roles table by executing Spree::Role.create(name: 'role_name') in the rails console;
  • In one of the configuration files (config/intializers/spree.rb, config/application.rb, db/migrations, db/seeds) add a line Spree::Role.find_or_create_by(name: 'role_name') for each role you wish to create.

With the user roles identified and created, we can proceed with assigning them permissions sets.

Permission sets

Addition of each new role to your application (e.g. :blog_editor, :promotion_manager, :retail_admin, etc) increases the overall authorization logic complexity. Solidus enhances the permission management process by solving three issues:

  • Enable one to split the Ability class into smaller chunks of related permissions;
  • Enable reuse by allowing one to mix and match various user roles with various permission sets;
  • Enable one to extend abilities (think pushing roles and permissions to an array), rather than directly overriding or decorating the Ability class;

For example, by extending PermissionSet::Base it is now possible to decouple the admin role permissions from the rest of the access control logic:

# lib/spree/permission_sets/super_user.rb
module Spree
  module PermissionSets
    class SuperUser < PermissionSets::Base
      def activate!
        can :manage, :all
      end
    end
  end
end

The same decoupling is then applied to the default user role permissions:

# lib/spree/permission_sets/default_customer.rb
module Spree
  module PermissionSets
    class DefaultCustomer < PermissionSets::Base
      def activate!
        can :display, Product
        can :display, Taxon
        can :display, Taxonomy
        can :display, Variant
      end
    end
  end
end

New permission sets should be created in their dedicated classes that extend Spree::PermissionSets::Base. Permission rules defined with the CanCan DSL should be created in the activate! method. Following the examples above, place your custom permission sets under the lib/spree/permission_sets folder. Finally, remember to load Solidus lib files in your application configuration:

# config/application.rb
config.before_initialize do
  Dir.glob(File.join(File.dirname(__FILE__), "../lib/spree/**/*.rb")) do |c|
    require_dependency(c)
  end
end

Role configuration

Permission sets organize related permissions without binding them to any user role. A role can be associated with a list of permission sets through a call to assign_permissions in the Solidus initializer:

# config/initializers/spree.rb
Spree.config do |config|
  config.roles.assign_permissions :admin, ['Spree::PermissionSets::SuperUser']
  config.roles.assign_permissions :default, ['Spree::PermissionSets::DefaultCustomer']
  ...
end

Conclusion

An authorization system is an integral part of any modern application that assumes user interaction. Both development and maintenance of authorization logic quickly become non-trivial with the growth of the application. Specifically, adding new roles and permission logic contributes to the increasing complexity of the system.

Solidus provides a built-in solution for user permission management. Extending the CanCan interface, the proposed approach allows one to group related permissions into separate sets and later assign them to different roles. Defining role permissions from smaller permission sets, or "componentization", allows to reduce the perceived complexity of the overall system while preserving a modular and easily maintainable system.

You may also like

Let’s redefine
eCommerce together.