Multi Tenancy with PostgreSQL RLS
The simplest way to build multi tenant applications ever. Create single tenant and multi tenant applications with the same code or no code at all.
What is Multi Tenancy?
Multi tenancy is a widely used architecture to create SaaS applications where the hardware and software resources are shared by the tenants. Software multi tenancy refers to a software architecture in which a single instance of software runs on a server and serves multiple tenants. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multi-tenant architecture, a software application is designed to provide every tenant a dedicated share of the instance including its data, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants.
A Tenant is a customer of the SaaS application that pays money to use the service.
The Host is the company that owns the SaaS application and manages the system.
What are the main benefits of multi tenant architecture?
Compared to single tenant architecture, in which each tenant gets its own instance of the application, database, and supporting hardware infrastructure, multi tenant architecture offers significant benefits to software providers and tenant customers. The main benefits are the following:
Lower costs
Because the software provider can serve multiple tenants from a single application instance and supporting infrastructure, ongoing costs are lower than those of a single tenant arrangement. Since each of the tenants is using the same application and database, the cost of development and maintenance lowers down for all the customers. A multi tenant application does not require new software resources and the setting up of a database for every increased tenant. The cost of onboarding a new tenant tends to be zero at the full scale.
Easier maintenance
The architecture of the multi tenant application is developed in such a way that it makes it far more convenient for the host to maintain the application. A host does not need to change the codes or the data structure because the shared codes are common. The cost of maintenance reduces as it is shared by the tenants when an update is needed. This takes place as all the tenants use the common code. With a multi-tenancy architecture, the host only has to make updates once to share them with all of its tenants.
What is the challenge with multi tenancy architecture?
Isolating tenant data is a fundamental responsibility for Software as a Service (SaaS) providers. One tenant should never gain access to another tenant’s data.
With the risks so great, it is critical to have an effective data isolation plan. In a shared database model, often the only choice is to rely on your software developers to implement the proper checks with every SQL statement written. Just like other security concerns, you want to enforce tenant data isolation policies in a more centralized manner that is less dependent on the everyday variability of your source code.
Multi tenancy Architecture
Codeless Platform supports Single Database architecture where all tenants are stored in a single database (and single schema). Tenant data isolation is achieved by using PostgreSQL RLS (Row Level Security).
PostgreSQL 9.5 and newer includes a feature called Row Level Security (RLS). When you define security policies on a table, these policies restrict which rows in that table are returned by SELECT
queries or which rows are affected by INSERT, UPDATE and DELETE commands. RLS policies have a name and are applied to and removed from a table with ALTER statements. The easiest way to understand RLS is to think of it as an automated WHERE clause that PostgreSQL manages itself.
Codeless Platform is using a single PostgreSQL application user (that is not a superuser). Multi tenancy is achieved by setting runtime parameter app.tenant_id that is scoped to the current connection session.
This allows having proper data isolation that does not depend on application code where a developer has to make sure to filter each SQL statement for a proper tenant. This isolation includes data exports and reports. Whatever HQL or SQL is performed from application code or inside any Report will be automatically filtered by the tenant on the database engine level. PostgreSQL RLS will also make sure that data protection would not allow SQL statements that try to read other tenant data by using conditions like: where tenant_id = ‘specific tenant’. It’s also not possible to change the tenant_id of the table with RLS applied unless you are a database superuser.
More about RLS on PostgreSQL official documentation: https://www.postgresql.org/docs/current/ddl-rowsecurity.html
Another good source of info on AWS blog: https://aws.amazon.com/blogs/database/multi-tenant-data-isolation-with-postgresql-row-level-security/
CodeLess Way
IN CODELESS PLATFORM APPLICATION CODE IS 100% MULTI TENANCY UNAWARE. YOUR CODE IS EXACTLY THE SAME FOR SINGLE AND MULTI TENANCY.
No need to use any special tenant API. No need to add any annotation on JPA classes. No need to force JPA classes to implement any tenant specific interface. No need to write any Hibernate filter.
In the multi tenant environment, you still benefit from all security features on all levels from assigning a user to applications to field-level security. An additional benefit is that you can define common security that will be applied for all tenants but also special rules for each tenant.
All resources like images, reports and attachments are also isolated between tenants in separated folders on the file system. This allows to easily track each tenant's use of disk space. The additional benefit in multi tenancy is that you can define common reports that are visible to all tenants but also allow custom reports per tenant.
Tenant registration tables
Two JPA classes are specific to the tenant environment:
-
Tenant
The tenant is used to register the tenant. This form is not visible to tenants, it can be accessed only by users with a valid license (you). This way data is protected from accidental use by unauthorized users. Valid to date is used to limit validity for a tenant. As soon as the date is valid to date, none of the tenant users would be able to log in to the application. This way you can easily manage subscriptions for using your application per tenant.
Note three colored labels in the footer: ‘development’, ‘Dev MULTI TENANT’ and ‘codeless’. These labels are visible only to licensed users, end-users of your application do not see them.
‘development’ is the mode your server is started (development or production). ‘ Dev MULTI TENANT’ is the name of the current database connection. Purple colored ‘codeless’ is the name of the current tenant and it is visible only in multi tenant environments.
-
UserLoginTenant
UserLoginTenant is used to register tenant users. This form is not visible to tenants, it can be accessed only by users with a valid license (you). This way data is protected from accidental use by unauthorized users. Valid to date is used to limit validity for tenant users. As soon as the date is valid to date, the tenant user would not be able to login into the application. This way you can manage subscriptions for using your application by a user per tenant.
We will add more ways to control tenants and users of tenants in the future, for example defining the maximum number of active users per tenant, maximum available disk space per tenant etc.
User login belongs to one tenant
User login can belong to only one tenant. When a user enters a name and password in the Login screen, there is no need to ask for the tenant as it is known by UserLoginTenant. User login is tenant aware and user that is logged to ‘company1’ tenant will see only users that also belong to ‘company1’ tenant. PostgreSQL RLS will make sure that no user of one tenant will ever see users of other tenants. Users with permission to add new users can add new users to the same tenant. After the user is added, UserLoginTenant will be updated with the new user on a current tenant. If the user deletes some user in the tenant, it will be removed from UserLoginTenant as well.
How Codeless Platform checks if the environment is multi tenant or not?
Codeless Platform knows if the environment is multi tenant or not according to Database Connection. You can set this property in Database Synchronization Tool.
Note the general rule that you should always use (database) superuser when you use Model Administrator and Database Synchronization Tool.
How to create an application user on PostgreSQL?
You can read about this in official PostgreSQL documentation. Here is an example of how you can do it:
Note that we are always using FORCE ROW LEVEL SECURITY so RLS will be applied for this user.
You should use this user (or similar) when you publish your application and deploy it for use in a multi tenant environment. You should never use system users(like Postgres) as RLS is not applied to system users.
How to make some table/form tenant aware?
Use Model Administrator to create Model Entity with property tenantId and column name TENANT_ID String(255)
When you create a form with Form Designer make sure that you select the multi-tenant option:
Note that we need to define tenancy on form level as we want to separate resources (images, attachments, uploads, report templates etc.) per tenant. This is explained later in this document.
How to add multi tenancy to database tables?
To define RLS for some tables, you need to alter the table with statements that will define RLS. You can do this in the PostgreSQL tool PSQL. Make sure that you apply the following naming convention as you will easier find the policy later:
ALTER TABLE tablename ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tablename_mt_policy ON tablename;
CREATE POLICY tablename_mt_policy ON tablename USING (tenant_id = current_setting('app.tenant_id')::VARCHAR);
ALTER TABLE tablename FORCE ROW LEVEL SECURITY;
Codeless Platform can also do this automatically for you. Remember to connect as a system user, go to Database Synchronization Tool, select PostgreSQL connection that is a single tenant and click “Multi tenant”:
You will see the script generated for you (note that you will see all tables that could be tenant aware i.e. that have column TENANT_ID):
You can execute the script by pressing the “Execute” button or copy this content and execute it in PSQL.
How to remove multi tenancy from database tables?
In order to remove RLS for some tables, you need to alter the table with statements that will disable RLS. You can do this in PostgreSQL tool PSQL. Make sure that you apply the following naming convention as you will easier find the policy later:
ALTER TABLE tablename DISABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tablename_mt_policy ON tablename;
Codeless Platform can do this automatically for you. Remember to connect as a system user, go to Database Synchronization Tool, select PostgreSQL connection that is multi tenant and click “Single tenant”:
You will see the script generated for you (note that you will see all tables that could be tenant aware i.e. that have column TENANT_ID):
You can execute the script by pressing the “Execute” button.
Codeless Platform Data Synchronization Tool can add or remove tenancy.
Is it possible to see the data from other tenants?
For tenants the answer is NO, no tenant can see other tenant's data.
If you start the same application with a multi tenant connection using a system user, you will see all data. You should never do this except when you are working with Model Administrator or Database Synchronization Tool but this does not have anything to do with application user that you should use when deploying an application in production.
For licensed users it is possible to change the tenant during the login session. This change will not be persisted and no tenant user will see that you entered their tenant as this information is stored in the session only.
If you are a licensed user and the connection is multi tenant, you can change the tenant in User Settings. Make sure that you are using an application user, you don’t need to use a superuser. Log in to your tenant and once you are logged in, go to User Settings:
As soon as you save the changes (that will not persist) it will reflect on the footer:
If you go to User logins, you will see only the users of ‘tenant’, you will not see the users of your real tenant ‘codeless’.
Codeless Platform tenant data is fully isolated and protected by RLS from other tenants.
Authorized users (host developer with license) can change tenants on a login session-level.
How can I define security that will be applied only for a particular tenant?
Log in and choose the tenant the way it is explained above. Open Form Designer and go to form that is tenant aware. In the toolbar you will see two different buttons for defining security permission. One is that will define common security rules for all tenants, and one (purple) is for the particular tenant:
When you click on common security you will get normal options like in the case for a single tenant:
I
In the case of tenant-specific security you will see the following:
Note that everything you learned in Security Administration is the same here, but in the case of a particular tenant, permission constraints will be applied only for a particular tenant. If some security constraint already exists for common permissions (applied for all tenants) it will not be offered for a particular tenant as there is no need to.
Note that when the Platform checks security it checks common and particular tenant security. If common and particular security constraint defines the same thing, the user will be granted the right if posses one of this permissions as they define the same thing.
Codeless Platform security supports tenant specific rules.
Form Designer will adopt automaticity in tenant environments to support tenant specific security.
Security roles in multi tenant environment:
When using security roles you can mix common and particular permissions but you don’t have to do that. You can create tenant specific roles where you can add only tenant specific permissions. It’s up to you how you would like to organize security roles. When you log in to a particular tenant, you will be able to add permissions that are common or particular for the current tenant.
If you change the tenant (the way described earlier) and look into permissions of some roles you will see only common and current tenant permissions. In other words, the same role like for example HR_MANAGER can have different permissions for different tenants. You administrate which permissions are part of this role by changing to a particular tenant, and the platform will know how to apply these permissions for each user on a particular tenant. Here is an example role when the tenant is ‘codeless’:
And the same role when tenant is ‘tenant’:
From the examples above you can see that in the ‘codeless’ tenant there is specific permission (to VIEW a Button field on ALLFieldsTest_MULTI form) that is defined only for this tenant and added to role ADMIN.
In the case of ‘tenant’ there is no specific permission and only common permissions will be checked for the same role ADMIN. We can define specific permissions for tenant ‘tenant’ and add it to the same role, other tenants like ‘codeless’ are not aware or affected by this action.
Codeless Platform security is tenant specific in the tenant environment.
Resources per tenant
In the Codeless platform all resources like user uploads (images, attachments, etc.), forms (HTML, CSS) and templates (report templates, email templates etc.) are stored in one centralized place. Some of the resources are the same for all tenants like forms as the HTML/CSS of forms are not multi tenant specific. Different versions of forms per tenant can be achieved by simply copying the form, changing it for a specific tenant and adding in a new version of the application menu that can be user specific.
Other resources like uploads have to be tenant specific but only for forms that are multi tenant aware. For example Form ‘Application’ is not multi tenant aware and images that are uploaded to represents specific applications are the same for all tenants. Form ‘User Login’ is tenant specific as each tenant belongs to a particular tenant. As the User login has a photo, this resource (image) when uploaded should be stored in tenant specific place.
Let’s illustrate this more with some forms that are not part of Codeless Platform. Let’s imagine we want to create a ‘Country’ form. This form, should not be tenant aware as it’s common for every tenant because the country is a country and it does not change per tenant. Another form ‘Partner’ should be tenant aware as every tenant (customer) can have it’s own partners (company or an individual).
All resources that each tenant uploads on tenant aware form (like ‘Partner’) will be on a tenant specific place, not on a common resource place. This way we can track how much disk space is used by each tenant and we can also choose to do a backup of all resources or only for a particular tenant.
Another benefit is that we can for example create a common report template for some form like “Invoice” that all tenants can see and use but at the same time allow each tenant to upload his own version of “Invoice template” that is isolated from other tenants and can be seen and used only by the tenant that uploaded the template. This is fully supported by the platform and the only rule that you should be aware of is that tenant is not able to delete common templates as it would be also removed for all other tenants because it’s a single file that all tenants are accessing. The following section illustrates how this works.
Report per tenant
Let’s illustrate this on the “UserLogin” form that belongs to the “System” application. This form is tenant aware.
A commonplace for this form is:
\reports\System\UserLogin
CommonUserLogin.docx
Specific tenant place for “codeless” tenant is:
\tenants\codeless\reports\System\UserLogin
CodelessUserLogin.docx
And specific tenant place for “company2” tenant is:
\tenants\company2\reports\System\UserLogin
Company2UserLogin.docx
When a user starts UserLogin form in tenant “codeless”, the available reports screen will look like:
As you can see, a user of “codeless” tenant can see common (CommonUserLogin.docx) and “codeless” reports (CodelessUserLogin.docx).
Let’s now add a new report. If we click on “settings” (cogs) icon we will see the Report manager form:
First, note that “Delete” is disabled for a selected common report. If we now click “Add” we can add a new report:
Let’s imagine that we uploaded “Codeless_2.docx”. Now Available reports will show the following content:
Specific tenant place for “codeless” tenant will now contain the following:
\tenants\codeless\reports\System\UserLogin
CodelessUserLogin.docx
Codeless_2.docx
Tenant ‘company2’ will see common reports (CommonUserLogin.docx) and ‘company2’ specific reports (Company2UserLogin.docx)
If other tenants do not have specific reports, they will see only (CommonUserLogin.docx) but if they upload their own versions, only they can see and use their specific reports.
Codeless Platform resources are isolated per tenant.
Codeless Platform Report Manager is tenant aware.
Summary
-
In the Codeless platform, the application code is fully unaware of the tenancy.
-
No need to use any tenant specific API, annotation, interface or filter.
-
Codeless Platform supports a Single Database architecture where all tenants are stored in a single database.
-
Tenant data isolation is achieved by using PostgreSQL RLS (Row Level Security).
-
Codeless Platform is using a single PostgreSQL application user, that is not a superuser.
-
Multi tenancy is achieved by setting runtime parameter app.tenant_id that is scoped to the current connection session.
-
Data Synchronization Tool can add or remove tenancy.
-
Security administration is tenant specific.
-
Form Designer will adopt automaticity in tenant environments to support tenant specific security.
-
Resources are isolated per tenant.
-
The report Manager is tenant aware.