loading...

How to secure a multitenant application architecture

wparad profile image Warren Parad Originally published at authress.io on ・4 min read

Multitenant architecture comparison

A Multitenant application

Multitenancy is the concept that your application serves distinct non-overlapping accounts, with resources assigned to and belonging to each account. A simple example of this is an off-line console video game. Each game copy is bought and paid for by a single owner (“account”), and in that copy there may be some amount of gameplay saved data, development data, and user configuration which is unique and sequestered to that copy. In some cases that data may need to be synced with other copies that the user owns. For example a PC version or potentially a status update on their mobile device.

On the opposite side of the spectrum there are open communities where no data is restricted to the users, a simplified version of Twitter is a good example. Tweets are public, anyone can read them. While a user may be able to post new tweets and delete ones they posted, the environment that user sees is a combination of all users on that platform.

In these extremes, access control policies aren’t that meaningful, since users either have access to nothing (except their resources) or everything. It becomes required and thus more challenging, when resources are partially shared. Take a photo sharing application. While photos may be owned by a single account, a user may want to share their photos with their friends. Additionally they could create albums which their whole family can see, or public ones they intend to demonstrate their photography prowess.

Users create separate accounts which have their own data, own configuration, but may need to share that data with users in that account, or users in other accounts. This is at the core of what is considered a multitenant application.

Application resources

If we use a document repository example we can start to build out what the recommendations might be to secure that data. To start off we’ll look at the types of actions users may want to perform, these are often called user stories.

  • A user can create an account and provision the necessary resources, add anything to that account, and additionally a user should be able to edit. They are the Owner of that account.
  • An account administrator would like to be able to add additional users to the account to manage the documents that are there.
  • An account manager needs to upload documents, change documents, and delete them.
  • A user in an account needs to be able to share a document with anyone else, or potentially just specific people, irrespective of the account the user is part of.

This is just some of the functionality necessary to be built into a document repository. While it is trivial to save documents (depending on their size), getting the permissions right so that they are both easy to maintain but can also provide the necessary flexibility for your users to control access they want to is difficult. We’ll walk through one possible implementation below.

Modeled Actions

  1. Create Account — Let’s assume no restricted access is necessary here, anyone can create an account.

  2. Invite User to Account — Perhaps something like

  • Resource: accounts/{accountId}/users
  • Permissions: AddUser/InviteUser, RemoveUser, ReadUsers
  1. List accounts — I’ll assume accounts are private. A user can only list accounts they are part of, potentially if you share a document a user might need to be able to see some aspects about that account that owns the document:
  • Resource: accounts/{accountId} and accounts/{accountId}/info
  • Permissions: updateName/Description, ReadAccount
  1. List documents — Need to be able to list the documents in the account
  • Resource: accounts/{accountId}/documents/(documentPath)
  • Permissions: AddDocument, DeleteDocument, ReadDocument, EditDocument
  1. List users — Need to be able to list users that have access to a document
  • Resource: accounts/{accountId}/documents/(documentPath)/members
  • Permissions: ShareDocument, RemoveAccess, UpdateAccess, AssignDocumentOwner

These resource uris are a good match for our user stories about a document repository. For these we would create the relevant roles. So far we listed out the permissions, since the permissions are checked by services we’ll want role abstractions that contain these permissions for the resources.

Relevant Roles

Account Admin:

  • Will own everything about an account
  • Permissions: *

Account Manager:

  • Can modify users and documents
  • Permissions: AddDocument, DeleteDocument, ReadDocument, EditDocument, ShareDocument, RemoveAccess, UpdateAccess, AssignDocumentOwner

Account Member:

  • Can modify documents
  • Permissions: AddDocument, DeleteDocument, ReadDocument, EditDocument, ShareDocument

Document Viewer:

  • Can read a document
  • Permissions: ReadDocument, (ShareDocument)

Relevant Resources

  • accounts/{accountId}/users
  • accounts/{accountId}/info
  • accounts/{accountId}/documents/(documentPath)

Standard Multitenant Resource Recommendations

It can be difficult to get the resource paths just right for your application. What’s important is matching up the user stories to necessary access control checks. Since Authress provides scoped permissions and resources, the best recommendation are resource uris that looks similar to the following:

NS:tenants/{tenantId}/parentResources/{parentResourceId}/resources/{resourceId}/subResources/{subResourceId}

  • NS is a custom namespace, your usage of security policies might span across different product spaces, if these are to be separate, prefixing them goes a long way.
  • We can separate each section of the path with hardcoded identifier. This is important so that resources of different types are easily distinguishable. If we had /{id}/{id2} it would not be possible to differentiate access to /resources/{id}/ sub /{id2} and /{accounts}/{id}/ resource /{id2}, since they look the same.
  • Always scope with the tenant. There are some situations where resources might be shared, but someone fundamentally one tenant/account always ows the resource.
  • Resources in Authress are cascading, so if there is a hierarchy relation between them, this is expressible in the resourceUri. This is a great way to automatically grant access to sub child resources when they are created without needing to create or updating access records. If a user has access to /resources/{id} then they will also have the same roles/permissions to all sub resources /resources/{id}/subResource/{sub1}

(Another example exists in the Zoom Case study)

In some cases resources are very fluid and too much scoping can be a problem, but in Authress these can be changed by updating access records and a simple migration can be used to propagate them if the access control model needs to be changed.

Originally published at https://authress.io on July 12, 2020.


Posted on by:

wparad profile

Warren Parad

@wparad

Long time software architect, creating application security plug-ins for any software application @ Rhosys. Talk to me about security in microservices. Feel free to DM me.

Discussion

markdown guide