Giter VIP home page Giter VIP logo

demo-multitenant-saas's Introduction

Multitenant SaaS platform demo

This demo shows how to use Cerbos's scoped policies (available in 0.13+) to model a multitenant SaaS platform, where each tenant may have specific authorization requirements.

Scenario

Resources

We're going to look at authorizing access to a single kind of resource: purchase orders.

Principals

On this SaaS platform, users can hold multiple roles, depending on how they are assigned to resources:

  • customer users are assigned to their tenant;
  • operations users (staff of the SaaS provider) are assigned to the tenants that they manage; and
  • manufacturer users belong to organizations, which are assigned to manufacture specific purchase orders.

It's possible for a user to be a customer in one tenant but also to belong to an organization that manufactures purchase orders for another tenant.

Actions

There are three actions we need to authorize on a purchase order resource:

  • prepareForDelivery may be performed by manufacturers assigned to the purchase order;
  • sendInvoice may be performed by operations assigned to the purchase order's tenant; and
  • view may be performed by manufacturers assigned to the purchase order, and customers or operations assigned to the purchase order's tenant.

Tenant-specific requirements

One tenant, Vanilla Ltd., doesn't have any authorization requirements beyond the base set of permissions described above.

The other, Regional Corp., is divided internally into APAC and EMEA regions. In this tenant, purchase orders are allocated to a region, and customer users are allocated to one or both regions. Customers should not be able to view purchase orders if they are not allocated to the purchase order's region.

Solution

We'll use scoped policies, and implement the base set of permissions in the root scope. Tenant-specific requirements can then be layered in policies scoped to the tenant's identifier. That way, applications requesting authorization decisions from Cerbos don't have to be aware of the tenant's authorization requirements - they just send the tenant identifier as the scope on the resources.

We'll use derived roles to convert the user's assignments into the role they have for the purchase order being authorized.

Principals

We'll send purchase order resources to Cerbos in this format:

Vanilla Ltd. customers

{
  "id": "[email protected]",
  "roles": ["user"],
  "attr": {
    "tenantAssignments": {
      "customer": ["vanilla"]
    }
  }
}

Regional Corp. customers

{
  "id": "[email protected]",
  "roles": ["user"],
  "attr": {
    "tenantAssignments": {
      "customer": ["regional"]
    },
    "regions": ["apac"] # and/or "emea"
  }
}

SaaS provider operations

{
  "id": "[email protected]",
  "roles": ["user"],
  "attr": {
    "tenantAssignments": {
      "operations": ["vanilla"] # and/or "regional"
    }
  }
}

Acme Inc. manufacturers

{
  "id": "[email protected]",
  "roles": ["user"],
  "attr": {
    "organizations": ["acme"]
  }
}

Resources

We'll send purchase order resources to Cerbos in this format:

{
  "id": "ABC-123",
  "kind": "purchase_order",
  "scope": "regional", # or "vanilla"
  "attr": {
    "tenant": "regional", # or "vanilla"
    "organizationAssignments": {
      "manufacturer": ["acme"]
    },
    "region": "apac" # or "emea"; omitted for the "vanilla" tenant
  }
}

Derived roles

We can derive the customer and operations roles by checking if the purchase order resource's tenant is in the principal's corresponding tenantAssignments:

apiVersion: api.cerbos.dev/v1
derivedRoles:
  name: tenant_assignments
  definitions:
    - name: customer
      parentRoles:
        - user
      condition:
        match:
          expr: R.attr.tenant in P.attr.tenantAssignments.customer

    - name: operations
      parentRoles:
        - user
      condition:
        match:
          expr: R.attr.tenant in P.attr.tenantAssignments.operations

We can derive the manufacturer role by checking for overlap between the purchase order resource's organizationAssignments and the principal's organizations:

apiVersion: api.cerbos.dev/v1
derivedRoles:
  name: organization_assignments
  definitions:
    - name: manufacturer
      parentRoles:
        - user
      condition:
        match:
          expr: hasIntersection(R.attr.organizationAssignments.manufacturer, P.attr.organizations)

Resource policies

With the derived roles in place, we can define the base set of permissions in a resource policy in the root scope:

apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: purchase_order
  importDerivedRoles:
    - organization_assignments
    - tenant_assignments
  rules:
    - actions:
        - prepareForDelivery
      effect: EFFECT_ALLOW
      derivedRoles:
        - manufacturer

    - actions:
        - sendInvoice
      effect: EFFECT_ALLOW
      derivedRoles:
        - operations

    - actions:
        - view
      effect: EFFECT_ALLOW
      derivedRoles:
        - customer
        - manufacturer
        - operations

For Vanilla Inc., we don't want to introduce any additional rules, so we can define an empty scoped policy (note that this requires Cerbos 0.14+; in 0.13, policies must define at least one rule):

apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: purchase_order
  scope: vanilla

For Regional Corp., we'll define a scoped policy that denies view access to customers who aren't allocated to the purchase order resource's region:

apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: purchase_order
  scope: regional
  importDerivedRoles:
    - tenant_assignments
  rules:
    - actions:
        - view
      effect: EFFECT_DENY
      derivedRoles:
        - customer
      condition:
        match:
          expr: |-
            !(R.attr.region in P.attr.regions)

The remaining rules will be enforced by falling back to the root policy. Note that we opted to deny non-matching regions in the scoped policy rather than allowing matching regions; this means we could introduce further conditions to the allow rule in the root policy without having to duplicate them in the scoped policy.

Success!

With these policies in place, we can now authorize the required actions, and the tenant-specific authorization requirements are encapsulated in Cerbos without leaking into our applications.

Try it out

You can clone this repository and run make to run a set of tests that exercise the policies. This requires Docker to be installed and authenticated to the GitHub container registry (ghcr.io). Alternatively, if you have Cerbos 0.14+ installed locally, you can run

$ cerbos compile --tests=tests policies

Learn more

demo-multitenant-saas's People

Contributors

haines avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

lucidsamuel

demo-multitenant-saas's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

None detected


  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.