Utilizing client extension for Postgres with Prisma to activate RLS: A step-by-step guide

Recently, I attempted to implement client extension as advised on Github. My approach involved defining row level security policies in my migration.sql file:

-- Enabling Row Level Security
ALTER TABLE "User" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "Company" ENABLE ROW LEVEL SECURITY;

-- Applying Row Level Security for table owners
ALTER TABLE "User" FORCE ROW LEVEL SECURITY;
ALTER TABLE "Company" FORCE ROW LEVEL SECURITY;

-- Defining row security policies
CREATE POLICY tenant_isolation_policy ON "Company" USING ("id" = current_setting('app.current_company_id', TRUE)::uuid);
CREATE POL

In addition, I have specified only two models in my schema.prisma file:

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["clientExtensions"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Company {
  id    String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  name  String
  users User[]
}

model User {
  id        String  @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  companyId String  @default(dbgenerated("(current_setting('app.current_company_id'::text))::uuid")) @db.Uuid
  email     String  @unique
  company   Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
}

Furthermore, my script.js file contains various functions aimed at validating the functionality of RLS. Despite creating test data for both company and user tables, I am facing difficulties in restricting access to companies or users as required.

//Setting a company_id as current_company_id for RLS Validation
const setUserId = await prisma.$executeRaw`SET app.current_company_id = 'id_of_a_company';`;
// Creating a specific company with a user
await prisma.company.create({
        data: {
          name: "Company 1",
          users: {
            create: {
              email: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e693958394d7a685898b9687889fd7c885898b">[email protected]</a>",
            },
          },
        },
      });
// Attempting to query users from Company 1 as a user who should have access
    const company1Users = await prisma.user.findMany({
        where: {
        company: {
            name: "Company 1",
        },
        },
    });
    // Trying to fetch users from Company 2 as a user without access privileges
    const company2Users = await prisma.user.findMany({
        where: {
        company: {
            name: "Company 2",
        },
        },
    });

Any insights on what could be going wrong here?

Answer №1

Here are two potential reasons for the issue:

First: Ensure you are using the extended PrismaClient

It appears that in your examples, you have not shown that you are extending the original PrismaClient with the necessary data from current_settings and then utilizing that client for queries.

For instance, this is how you can configure the extended client to utilize the current_settings:

const db = new PrismaClient()
const rlsDb = db.$extends(currentCompanyRlsExtension(session.company.id))

function currentCompanyRlsExtension(companyId) {
  return Prisma.defineExtension((prisma) =>
    prisma.$extends({
      query: {
        $allModels: {
          async $allOperations({ args, query }) {
            const [, result] = await prisma.$transaction([
              prisma.$executeRaw`SELECT set_config('app.current_company_id', ${companyId}, TRUE)`,
              query(args),
            ])
            return result
          },
        },
      },
    }),
  )
}

When making database queries, ensure to use the extended client:

const companyUsers = await rlsDb.user.findMany()

Second: Confirm that you are using a database user that cannot bypass RLS

Verify that your DATABASE_URL does not employ a database user that will consistently bypass RLS. According to the PostgreSQL v16 documentation:

Superusers and roles with the BYPASSRLS attribute always skip the row security system when accessing a table. Table owners typically bypass row security as well, unless they choose to abide by row security with ALTER TABLE ... FORCE ROW LEVEL SECURITY.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to programmatically trigger a CodeAction from a VSCode extension?

Can I trigger another extension's code action programmatically from my VSCode extension? Specifically, I want to execute the resolveCodeAction of the code action provider before running it, similar to how VSCode handles Quick Fixes. Most resources su ...

The Angular component seems to be failing to refresh the user interface despite changes in value

Recently, I created a simple component that utilizes a variable to manage its state. The goal is to have the UI display different content based on changes in this variable. To test this functionality, I implemented the component and used a wait function to ...

TypeScript's standard React.Children interface for compound components

One of my components is a Table, which can have children that are Column components: <Table data={data}> <Column cell={(c) => c.date} header="Date" /> <Column cell={(c) => c.count} header="Count" /> & ...

How to fix the TS4090 error regarding conflicting definitions for a node in Visual Studio 2017

My TypeScript project is building and running, but I'm encountering a multitude of build errors all originating from one issue: TS4090: (TS) Conflicting definitions for 'node' found at 'C:/[projectpath]/node_modules/@types/node/index ...

Guide on mocking a function inside another function imported from a module with TypeScript and Jest

I have a function inside the action directory that I want to test: import { Action, ActionProgress, ActionStatus, MagicLinkProgress } from '../../interfaces' import { areSameActions } from '../actionsProgress' export const findActionPr ...

The functionality of Angular/Typescript class.name appears to fail during a production build

Using Angular 5, I encountered an unusual problem with the class.name property. We have a TypeScript function as shown below: export class ApiService { public list<T>(c: new(values: Object)=> T) { var cname = c.name; .... } } When ...

Leveraging the OpenLayers Map functionality within an Angular service

I am curious to learn if there is a way to utilize a service in Angular for creating an OpenLayers map and then passing that service to other components to update the map based on interactions within those components. I have outlined my approach below. Des ...

Looking for Assistance with PyGreSQL's upsert() Function

Currently, I am running CentOS-6 with Python 3.8, PostgreSQL 12, and PyGreSQL 5.2.2. In previous versions, my code included a function that would insert a row of data if an update raised an exception due to the row not already existing. import pg db = pg. ...

Fetching an item from Local Storage in Angular 8

In my local storage, I have some data stored in a unique format. When I try to retrieve the data using localStorage.getItem('id');, it returns undefined. The issue lies in the way the data is stored within localStorage - it's structured as a ...

Struggling to convert my VueJS component from JavaScript to TypeScript, feeling a bit lost

I am new to VueJS and I am facing a challenge converting my VueJS project to use TypeScript. I have been trying to bind functions to certain variables in JavaScript, but I am struggling with accomplishing the same in TypeScript. Even though there are no er ...

Encountering difficulty when integrating external JavaScript libraries into Angular 5

Currently, I am integrating the community js library version of jsplumb with my Angular 5 application (Angular CLI: 1.6.1). Upon my initial build without any modifications to tsconfig.json, I encountered the following error: ERROR in src/app/jsplumb/jspl ...

I am looking to conceal the y-axis labels and tooltip within the react chart

I am currently working with react-chart-2. I have a line graph that displays a tooltip when hovered over, but I would like to hide this tooltip feature. Additionally, I want to remove the numbers 0, 0.1, 0.2 up to 1 on the left side (y-axis) of the gra ...

React Native is throwing a TypeError because it is encountering an undefined object

React Native is throwing an error claiming Undefined is not an object when it's clearly an object!! I'm confused about what's happening. Take a look at the code snippet below. Scroll down to the render() function. You'll see the follow ...

What is the reason behind being unable to register two components with the same name using React Hook Form?

I have encountered an issue while using the useForm hook from React Hook Form library. Due to the specific UI library I am using, I had to create custom radio buttons. The problem arises when I try to register two components with the same name in the form ...

JS/Docker - The attribute 'user' is not recognized in the context of 'Session & Partial<SessionData>'

I'm attempting to integrate express-session into my Node.js application running within Docker. I've come across several discussions on the topic: Express Session: Property 'signin' does not exist on type 'Session & Partial<Se ...

Utilizing classes as types in TypeScript

Why is it possible to use a class as a type in TypeScript, like word: Word in the provided code snippet? class Dict { private words: Words = {}; // I am curious about this specific line add(word: Word) { if (!this.words[word.term]) { this.wor ...

Issues with the rating plugin functionality in Ionic 3

After following the instructions in this tutorial: http://ionicframework.com/docs/native/app-rate/ Even though I implemented the second method, I encountered the following error: Uncaught (in promise): TypeError: Cannot read property 'split' ...

Managing status in Angular applications

I am currently working on a project using Angular 7 and I have the following code snippet: public deleteId(pId){ return this.http.delete<any>(this.deleteUrl(pId), {observe: 'response'}) .pipe(catchError(this.handleError)); } I ...

What are some solutions to the error message "Error: Cannot find any matching routes" that appears when trying to switch between tabs following a successful login?

I am facing an issue with my Ionic 4 (4.10.2 with Angular 7.3.1) app where I want to make it accessible only after login. Following a tutorial from , I encountered a problem when trying to access the ion-tabs section of my app. Chrome keeps showing this er ...

Can you provide guidance on how to divide a series of dates and times into an array based

Given a startDate and an endDate, I am looking to split them into an array of time slots indicated by the duration provided. This is not a numerical pagination, but rather dividing a time range. In my TypeScript code: startDate: Date; endDate: Date; time ...