Flexible Role-based Access Control

Business logic sometimes dictates that a provider has minimal access the billing profile of a customer and sometimes a provider must be able to update the credit card associated to a customer while on a phone call with that customer.

In order to support multiple usage patterns and security constraints, authorization is not embeded into the djaodjin-saas logic but rather implemented as URL decorators. It is the responsability of the developper to associate decorators to URLs as dictated by the business requirements.

The security framework defines a generic RoleDescription whose purpose is to define a role a User has on an Organization (see grant and request roles).

A organization-agnostic manager role always exists and helps with bootstrapping the security policies. In most setups a second role, for example, a contributor role is implemented. Typically manager have full access to an Organization while contributor are restricted to read-only permissions.

Examples

Let’s say you want to give POST access to contributors on /api/billing/charges/:charge/refund/, you would write the following in your urls.py:

from urldecorators import url
from saas.api.charges import ChargeRefundAPIView

urlpatterns = [

    url(r'^billing/charges/(?P<charge>[a-zA-Z0-9_\-\+\.]+)/refund/',
        ChargeRefundAPIView.as_view(),
        name='saas_api_charge_refund',
        decorators=['saas.decorators.requires_provider_weak']),
]

The previous example uses django-urldecorators and a saas.decorators.requires_provider_weak decorator.

The saas.urls module has been split in “common” set of functionalities such that in many cases you can decorate each include() with an appropriate decorator instead of each URL one by one. (ex: testsite/urls.py)

A blog post on Django Rest Framework, AngularJS and permissions might also be a useful read.

Decorators Available

The access control logic is best configured in the site URLConf through extensions like django-urldecorators. This is not only more flexible but also make security audits a lot easier.

saas.decorators.requires_agreement(function=None, agreement='terms-of-use', redirect_field_name='next')

Decorator for views that checks that the user has signed a particular legal agreement, redirecting to the agreement signature or log-in page if necessary.

saas.decorators.requires_paid_subscription(function=None, organization_kwarg_slug='organization', plan_kwarg_slug='subscribed_plan', redirect_field_name='next', strength=1, roledescription=None)

Decorator that checks a specified subscription is paid. It redirects to an appropriate page when this is not the case:

  • Payment page when no charge is associated to the subscription,
  • Update Credit Card page when charge.status is failed,
  • Waiting page when charge.status is in-progress.
saas.decorators.requires_direct(function=None, roledescription=None, redirect_field_name='next')

Decorator for views that checks that the authenticated request.user is a direct roledescription (ex: contributor) or manager for the Organization associated to the request.

Managers can issue all types of requests (GET, POST, etc.). while roledescription (ex: contributors) are restricted to GET requests.

_images/perms-contrib.svg
saas.decorators.requires_provider(function=None, roledescription=None, redirect_field_name='next')

Decorator for views that checks that the request authenticated User is a roledescription (ex: contributor) or manager for the Organization associated to the request itself or a roledescription (or manager) to a provider for the Organization associated to the request.

Managers can issue all types of requests (GET, POST, etc.). while roledescription (ex: contributors) are restricted to GET requests.

_images/perms-contrib-subscribes.svg
saas.decorators.requires_self_provider(function=None, roledescription=None, redirect_field_name='next')

Decorator for views that checks that the request authenticated User is the user associated to the URL. Authenticated users that can also access the URL through this decorator are roledescription (ex: contributors) or managers for any Organization associated with the user served by the URL (the accessed user is a direct roledescription or manager of the organization) and transitively contributors (or managers) for any provider to one of these direct organizations.

Managers can issue all types of requests (GET, POST, etc.). while roledescription (ex: contributors) are restricted to GET requests.

_images/perms-self-contrib-subscribes.svg

Design Note

We used to decorate the saas views with the “appropriate” decorators, except in many projects appropriate had a different meaning. It turns out that the access control logic is better left to be configured in the site URLConf through extensions like django-urldecorators. This is not only more flexible but also make security audits a lot easier.