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.
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
- Manually add a row in the
spree_rolestable by executing
Spree::Role.create(name: 'role_name')in the rails console;
- In one of the configuration files (
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.
Addition of each new role to your application (e.g.
:retail_admin, etc) increases the overall authorization
logic complexity. Solidus enhances the permission management process by solving
- 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
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
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
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
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.