DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

How does AI transform chaos engineering from an experiment into a critical capability? Learn how to effectively operationalize the chaos.

Data quality isn't just a technical issue: It impacts an organization's compliance, operational efficiency, and customer satisfaction.

Are you a front-end or full-stack developer frustrated by front-end distractions? Learn to move forward with tooling and clear boundaries.

Developer Experience: Demand to support engineering teams has risen, and there is a shift from traditional DevOps to workflow improvements.

Related

  • Formulating a Robust Strategy for Storage in Amazon Relational Database Service PostgreSQL Deployments
  • Just Use PostgreSQL, a Quick-Start Guide: Exploring Essential and Extended Capabilities of the Most Beloved Database
  • Understanding RDS Costs
  • How to Build a Full-Stack App With Next.js, Prisma, Postgres, and Fastify

Trending

  • Automating Kubernetes RBAC Sync With LDAP Entitlements Using Python
  • OTel Me Why: The Case for OpenTelemetry Beyond the Shine
  • Beyond Bytecode: Exploring the Relationship Between JVM, JIT, and Performance
  • Revolutionizing Software Development: Agile, Shift-Left, and Cybersecurity Integration
  1. DZone
  2. Data Engineering
  3. Databases
  4. Multi-Tenant Data Isolation and Row Level Security

Multi-Tenant Data Isolation and Row Level Security

Learn how Row Level Security (RLS) enforces data isolation in multi-tenant SaaS systems with a shared database model to improve security and reduce errors.

By 
Rajind Ruparathna user avatar
Rajind Ruparathna
·
Jan. 23, 25 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
6.9K Views

Join the DZone community and get the full member experience.

Join For Free

Over the past one and a half years, I was involved in designing and developing a multi-tenant treasury management system. In this article, I will share our approaches to the data isolation aspect of our multi-tenant solution and the learnings from it.

Background and Problem Regarding Data Isolation

Before going into the problem that I will focus on today, I must first give some background into our architecture for storage and data in our system. When it comes to data partitioning for SaaS systems, at the extreme far right end, we have the approach of using dedicated databases for each tenant (silo model), and on the other side of the spectrum is the shared database model (pool model).

Shared database model (Pool model)

Shared database model (Pool model)

Dedicated database model (Silo model)

Dedicated database model (Silo model)


For obvious reasons such as reduced management overhead and lower cost, it was decided that our solution would be in the shared database model, and with that comes the drawback of lower levels of inherent data isolation. 

In most cases with the shared database model, the data isolation depends on developers implementing the correct WHERE clauses in every SQL statement, but this is, of course, error-prone. We wanted an approach to enforce the data isolation from a separate layer and in comes the concept of Row Level Security (RLS).

Kotlin
 
interface TransactionRepository : JpaRepository<Transaction, Long> {

    // trivial approach
    fun findAllByTenantId(): List<Transaction>
    
    // what we want to achieve
    override fun findAll(): List<Transaction>
}


Solution: Row Level Security (RLS)

In general terms, Row Level Security (in RDBMS) refers to mechanisms that allow controlling access to rows in a database table based on some context (for example, tenant_id). To my knowledge, this feature has been around on PostgreSQL DB since 2020, and it is available in SQL Server, in Amazon Aurora for PostgreSQL, and RDS for PostgreSQL at the time of writing.

PLSQL
 
ALTER TABLE gtms_payment.TRANSACTION ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_policy ON gtms_payment.TRANSACTION
    USING (business_group_code = current_setting('app.current_tenant')::VARCHAR);


Other Solutions: Sping Post Filter, Hibernate Filters With Spring Aspects

To give more details from my analysis of the possible solutions, let me share some brief info on other possible approaches as well. One of the options was Spring PostFilter, but that would mean all data (across multiple tenants) would be returned to the backend from the db query before they are filtered and sent out, and this was not ideal for our use case.

Another option (which is specific to our backend tech stack — Spring, Hibernate, and Java/Kotlin) was to use Hibernate Filters with a Spring Aspect. This option was quite good but not as strong as having the enforcement done on the DB layer. (We did use this approach to solve a similar use case, and I hope to cover this in a later blog)

Going Ahead With RLS

Our standard database solution throughout the organization was PostgreSQL (for use cases like ours), and this helped in our decision to go ahead with RLS as the solution, but if you are trying to solve a similar problem with RLS, do take note that not all of the relational databases support Row Level Security yet so you might have to be aware of being locked into a subset of databases that support RLS. However, switching to another relational DB that supports RLS should not be too difficult as there is little dependency on the DB type on the code level itself when using RLS.

Implementation

Going into the implementation details, once RLS is enabled on the DB, we should set the parameters (in our case, app.current_tenant) for the DB to run the policy on for each query. In our case, we do that on the DataSource implementation as follows. In our flow, we maintain the Tenant information on Spring Security Context (again, which I hope to cover in a separate blog), and the getTenantId() which is abstracted out here is retrieving the Tenant Id from the Spring Security Context.

Kotlin
 
class TenantAwareHikariDataSource : HikariDataSource() {

    override fun getConnection(): Connection? {
        val connection = super.getConnection()
        return createConnection(connection)
    }

    override fun getConnection(username: String, password: String): Connection? {
        val connection = super.getConnection(username, password)
        return createConnection(connection)
    }

    private fun createConnection(connection: Connection): Connection? {
        connection.createStatement().use { sql ->
            sql.execute("SET app.current_tenant = '${getTenantId()}'")
        }
        return connection
    }
}


Note that if the parameters required for the RLS policy are not set, we will run into errors as follows:

Plain Text
 
[2024-08-25 22:53:50] [42704] ERROR: unrecognized configuration parameter "app.current_tenant"


Supporting Use Cases That Do Not Require a Tenant Id

In all multi-tenant systems, there will always be some use cases, such as tenant onboarding, where you will have to run DB queries without a Tenant Id. So, can we support such use cases? The answer is yes! In PostgreSQL functionality, the schema owner bypasses RLS policies. Hence, in our solution we have, we maintain two sets of DB configs (Tenant DB Config and Non Tenant DB Config) and two sets of Spring repositories tied to each DB config with tenant config as primary.

Thus, in the few use cases that need to run queries without a Tenant Id, we use the Non-Tenant configs. We also have to use PostgreSQL schema owner user for the database change management tool (in our case, Liquibase).

Pros and Cons of the RLS Approach to Sum Things Up

Pros

  • RLS provides data isolation on the DB layer. Hence, it provides a stronger isolation than that of the common WHERE clause approach.
  • Most of the data isolation logic (Tenant-aware data source, Tenant and Non-Tenant DB configs, etc.) can be on a separate library and shared across different micro-services. Hence, developers can worry less about tenant data isolation while developing application-level business logic.

Cons

  • Since not all relational databases support RLS yet, there will be a DB lock-in, as explained in previous sections.

I hope you all have fun with RLS! :-)

References

  1. Multi-tenant data isolation with PostgreSQL Row Level Security
  2. Architectural approaches for storage and data in multitenant solutions
Database Relational database Spring Security Isolation (database systems) PostgreSQL

Published at DZone with permission of Rajind Ruparathna, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Formulating a Robust Strategy for Storage in Amazon Relational Database Service PostgreSQL Deployments
  • Just Use PostgreSQL, a Quick-Start Guide: Exploring Essential and Extended Capabilities of the Most Beloved Database
  • Understanding RDS Costs
  • How to Build a Full-Stack App With Next.js, Prisma, Postgres, and Fastify

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: