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 executingSpree::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 lineSpree::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.