Skip to content

Commit 2900db2

Browse files
sru-thybharathkeyvalue
authored andcommitted
FEAT: add postgres rls for tenant isolation
1 parent 1e4d149 commit 2900db2

File tree

5 files changed

+82
-4
lines changed

5 files changed

+82
-4
lines changed

src/authentication/authentication.guard.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
22
import { GqlExecutionContext } from '@nestjs/graphql';
33
import { AuthenticationHelper } from './authentication.helper';
4-
import { ExecutionManager } from '../util/execution.manager';
54

65
@Injectable()
76
export class AuthGuard implements CanActivate {
@@ -14,7 +13,6 @@ export class AuthGuard implements CanActivate {
1413
if (token) {
1514
const reqAuthToken = token.split(' ')[1];
1615
ctx.user = this.authenticationHelper.validateAuthToken(reqAuthToken);
17-
ExecutionManager.setTenantId(ctx.user.tenantId);
1816
return true;
1917
}
2018
}

src/authorization/entity/abstract.tenant.entity.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { Column } from 'typeorm';
22
import BaseEntity from './base.entity';
33

44
class AbstractTenantEntity extends BaseEntity {
5-
@Column({ type: 'uuid' })
5+
@Column({
6+
type: 'uuid',
7+
default: () => "current_setting('app.current_tenant')",
8+
})
69
public tenantId!: string;
710
}
811

src/database/database.module.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
22
import { TypeOrmModule } from '@nestjs/typeorm';
33
import { ConfigModule, ConfigService } from '@nestjs/config';
44
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
5+
import { ExecutionManager } from '../util/execution.manager';
56

67
@Module({
78
imports: [
@@ -22,6 +23,18 @@ import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
2223
namingStrategy: new SnakeNamingStrategy(),
2324
synchronize: false,
2425
logging: true,
26+
extra: {
27+
poolMiddleware: async (query: string, params: any[]) => {
28+
const tenantId = ExecutionManager.getTenantId();
29+
if (tenantId) {
30+
return [
31+
`SELECT set_config('app.tenant_id', ${tenantId}, false) ${query}`,
32+
params,
33+
];
34+
}
35+
return [query, params];
36+
},
37+
},
2538
}),
2639
}),
2740
],

src/middleware/executionId.middleware.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@ import { NestMiddleware } from '@nestjs/common';
22
import { NextFunction, Request, Response } from 'express';
33

44
import { ExecutionManager } from '../util/execution.manager';
5+
import { AuthenticationHelper } from '../authentication/authentication.helper';
56

67
export class ExecutionContextBinder implements NestMiddleware {
8+
constructor(private readonly auth: AuthenticationHelper) {}
79
async use(req: Request, res: Response, next: NextFunction) {
8-
ExecutionManager.runWithContext(next);
10+
ExecutionManager.runWithContext(async () => {
11+
const token = req.headers.authorization?.split(' ')[1];
12+
if (token) {
13+
const user = this.auth.validateAuthToken(token);
14+
ExecutionManager.setTenantId(user.tenantId);
15+
}
16+
next();
17+
});
918
}
1019
}

src/migrations/1733833844028-MultiTenantFeature.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,64 @@ export class MultiTenantFeature1733833844028 implements MigrationInterface {
7171
await queryRunner.query(
7272
`ALTER TABLE "user_permission" ALTER COLUMN "tenant_id" DROP DEFAULT`,
7373
);
74+
await queryRunner.query(`
75+
ALTER TABLE "user" ENABLE ROW LEVEL SECURITY;
76+
ALTER TABLE "role" ENABLE ROW LEVEL SECURITY;
77+
ALTER TABLE "group" ENABLE ROW LEVEL SECURITY;
78+
ALTER TABLE "entity_model" ENABLE ROW LEVEL SECURITY;
79+
ALTER TABLE "user_group" ENABLE ROW LEVEL SECURITY;
80+
ALTER TABLE "user_permission" ENABLE ROW LEVEL SECURITY;
81+
ALTER TABLE "group_role" ENABLE ROW LEVEL SECURITY;
82+
ALTER TABLE "group_permission" ENABLE ROW LEVEL SECURITY;
83+
ALTER TABLE "role_permission" ENABLE ROW LEVEL SECURITY;
84+
ALTER TABLE "entity_permission" ENABLE ROW LEVEL SECURITY;
85+
86+
CREATE POLICY tenant_isolation_policy ON "user"
87+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
88+
CREATE POLICY tenant_isolation_policy ON "role"
89+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
90+
CREATE POLICY tenant_isolation_policy ON "group"
91+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
92+
CREATE POLICY tenant_isolation_policy ON "entity_model"
93+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
94+
CREATE POLICY tenant_isolation_policy ON "user_group"
95+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
96+
CREATE POLICY tenant_isolation_policy ON "user_permission"
97+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
98+
CREATE POLICY tenant_isolation_policy ON "group_role"
99+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
100+
CREATE POLICY tenant_isolation_policy ON "group_permission"
101+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
102+
CREATE POLICY tenant_isolation_policy ON "role_permission"
103+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
104+
CREATE POLICY tenant_isolation_policy ON "entity_permission"
105+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
106+
`);
74107
}
75108

76109
public async down(queryRunner: QueryRunner): Promise<void> {
110+
await queryRunner.query(`
111+
DROP POLICY tenant_isolation_policy ON "user";
112+
DROP POLICY tenant_isolation_policy ON "role";
113+
DROP POLICY tenant_isolation_policy ON "group";
114+
DROP POLICY tenant_isolation_policy ON "entity_model";
115+
DROP POLICY tenant_isolation_policy ON "user_group";
116+
DROP POLICY tenant_isolation_policy ON "user_permission";
117+
DROP POLICY tenant_isolation_policy ON "group_role";
118+
DROP POLICY tenant_isolation_policy ON "group_permission";
119+
DROP POLICY tenant_isolation_policy ON "role_permission";
120+
DROP POLICY tenant_isolation_policy ON "entity_permission";
121+
ALTER TABLE "user" DISABLE ROW LEVEL SECURITY;
122+
ALTER TABLE "role" DISABLE ROW LEVEL SECURITY;
123+
ALTER TABLE "group" DISABLE ROW LEVEL SECURITY;
124+
ALTER TABLE "entity_model" DISABLE ROW LEVEL SECURITY;
125+
ALTER TABLE "user_group" DISABLE ROW LEVEL SECURITY;
126+
ALTER TABLE "user_permission" DISABLE ROW LEVEL SECURITY;
127+
ALTER TABLE "group_role" DISABLE ROW LEVEL SECURITY;
128+
ALTER TABLE "group_permission" DISABLE ROW LEVEL SECURITY;
129+
ALTER TABLE "role_permission" DISABLE ROW LEVEL SECURITY;
130+
ALTER TABLE "entity_permission" DISABLE ROW LEVEL SECURITY;
131+
`);
77132
await queryRunner.query(
78133
`ALTER TABLE "user_permission" DROP COLUMN "tenant_id"`,
79134
);

0 commit comments

Comments
 (0)