'How to set a postgres configuration with Diesel? I'm trying get RLS to work
I have this "almost" working. I have setup all the infrastructure in my migrations:
-- tenant
CREATE TABLE tenant (
tenant_id int primary key generated always as identity,
name VARCHAR(255) UNIQUE,
status VARCHAR(64) CHECK (status IN ('active', 'suspended', 'disabled'))
);
-- Turn on RLS
ALTER TABLE tenant ENABLE ROW LEVEL SECURITY;
-- Restrict read and write actions so tenants can only see their rows
-- This policy implies a WITH CHECK that matches the USING clause
CREATE POLICY tenant_isolation_policy ON tenant
USING (tenant_id = current_setting('app.current_tenant')::int);
-- tenant_user
CREATE EXTENSION pgcrypto; -- needed for password
CREATE TABLE tenant_user (
user_id int primary key generated always as identity,
tenant_id int NOT NULL REFERENCES tenant (tenant_id) ON DELETE RESTRICT,
email VARCHAR(255) NOT NULL UNIQUE,
given_name VARCHAR(255) NOT NULL CHECK (given_name <> ''),
family_name VARCHAR(255) NOT NULL CHECK (family_name <> ''),
password TEXT NOT NULL,
is_verified BOOL DEFAULT 'f',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Restrict read and write actions so tenants can only see their rows
-- This policy implies a WITH CHECK that matches the USING clause
ALTER TABLE tenant_user ENABLE ROW LEVEL SECURITY;
-- Restrict read and write actions so tenants can only see their rows
-- This policy implies a WITH CHECK that matches the USING clause
CREATE POLICY tenant_user_isolation_policy ON tenant_user
USING (tenant_id = current_setting('app.current_tenant')::int);
This is the part I can't figure out how to get working with Diesel. I can do this from my SQL editor;
-- with super-user account
INSERT INTO tenant (name, status) VALUES ('tenant1', 'active');
INSERT INTO tenant (name, status) VALUES ('tenant2', 'active');
-- with non super-user account
SET app.current_tenant = 1;
INSERT INTO tenant_user (email, password, tenant_id, given_name, family_name) VALUES ('[email protected]', crypt('test', gen_salt('bf')), 1, 'Test', 'User');
SELECT * FROM tenant_user WHERE ('[email protected]' in (email)) AND password = crypt('test', password);
-- I get back the user I created
SET app.current_tenant = 2;
SELECT * FROM tenant_user WHERE ('[email protected]' in (email)) AND password = crypt('test', password);
-- I don't get back the user, this is what I expected
However, I can't seem to figure out how to do the SET configuration from Diesel. I tried something like this:
let sql = "SET app.current_tenant = 1";
let query = sql_query(sql);
let result = query.execute(&conn);
Which seems to run, but then my follow-up query does not return successfully as I was expecting:
sql_function!(fn crypt(passwd: Text, salt: Text) -> Text);
tenant_user::table()
.filter(email.eq(msg.email))
.filter(password.eq(crypt(msg.password, password)))
.get_result::<TenantUser>(&conn)
My query does work if I run it with the super-user account. So I think I'm missing something. Either my SET command is not running properly or I'm missing something else. Any help you could provide would be much appreciated.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
