rls
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
-- Move tables and types back to the public schema
|
||||
ALTER TABLE tenant.webdav_tokens SET SCHEMA public;
|
||||
ALTER TABLE tenant.user_memberships SET SCHEMA public;
|
||||
ALTER TABLE tenant.refresh_tokens SET SCHEMA public;
|
||||
ALTER TABLE tenant.tags SET SCHEMA public;
|
||||
ALTER TABLE tenant.document_correspondents SET SCHEMA public;
|
||||
ALTER TABLE tenant.document_tags SET SCHEMA public;
|
||||
ALTER TABLE tenant.document_asset_objects SET SCHEMA public;
|
||||
ALTER TABLE tenant.document_assets SET SCHEMA public;
|
||||
ALTER TABLE tenant.document_versions SET SCHEMA public;
|
||||
ALTER TABLE tenant.documents SET SCHEMA public;
|
||||
ALTER TABLE tenant.folders SET SCHEMA public;
|
||||
ALTER TABLE tenant.correspondents SET SCHEMA public;
|
||||
|
||||
ALTER FUNCTION shared.touch_jobs_updated_at() SET SCHEMA public;
|
||||
|
||||
ALTER TABLE shared.magic_tokens SET SCHEMA public;
|
||||
ALTER TABLE shared.jobs SET SCHEMA public;
|
||||
ALTER TABLE shared.webauthn_challenges SET SCHEMA public;
|
||||
ALTER TABLE shared.user_passkeys SET SCHEMA public;
|
||||
ALTER TABLE shared.users SET SCHEMA public;
|
||||
ALTER TABLE shared.tenants SET SCHEMA public;
|
||||
|
||||
ALTER TYPE shared.magic_token_kind SET SCHEMA public;
|
||||
ALTER TYPE shared.tenant_status SET SCHEMA public;
|
||||
|
||||
DROP SCHEMA IF EXISTS tenant CASCADE;
|
||||
DROP SCHEMA IF EXISTS shared CASCADE;
|
||||
@@ -0,0 +1,45 @@
|
||||
CREATE SCHEMA IF NOT EXISTS shared;
|
||||
CREATE SCHEMA IF NOT EXISTS tenant;
|
||||
|
||||
-- Move global types and tables into the shared schema
|
||||
ALTER TYPE tenant_status SET SCHEMA shared;
|
||||
ALTER TYPE magic_token_kind SET SCHEMA shared;
|
||||
|
||||
ALTER TABLE tenants SET SCHEMA shared;
|
||||
ALTER TABLE users SET SCHEMA shared;
|
||||
ALTER TABLE user_passkeys SET SCHEMA shared;
|
||||
ALTER TABLE webauthn_challenges SET SCHEMA shared;
|
||||
ALTER TABLE jobs SET SCHEMA shared;
|
||||
ALTER TABLE magic_tokens SET SCHEMA shared;
|
||||
|
||||
ALTER FUNCTION touch_jobs_updated_at() SET SCHEMA shared;
|
||||
|
||||
-- Move tenant-scoped tables into the tenant schema
|
||||
ALTER TABLE correspondents SET SCHEMA tenant;
|
||||
ALTER TABLE folders SET SCHEMA tenant;
|
||||
ALTER TABLE documents SET SCHEMA tenant;
|
||||
ALTER TABLE document_versions SET SCHEMA tenant;
|
||||
ALTER TABLE document_assets SET SCHEMA tenant;
|
||||
ALTER TABLE document_asset_objects SET SCHEMA tenant;
|
||||
ALTER TABLE document_tags SET SCHEMA tenant;
|
||||
ALTER TABLE document_correspondents SET SCHEMA tenant;
|
||||
ALTER TABLE tags SET SCHEMA tenant;
|
||||
ALTER TABLE refresh_tokens SET SCHEMA tenant;
|
||||
ALTER TABLE user_memberships SET SCHEMA tenant;
|
||||
ALTER TABLE webdav_tokens SET SCHEMA tenant;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'papercrate_app') THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
GRANT USAGE ON SCHEMA shared TO papercrate_app;
|
||||
GRANT USAGE ON SCHEMA tenant TO papercrate_app;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA shared TO papercrate_app;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA tenant TO papercrate_app;
|
||||
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA shared GRANT SELECT ON TABLES TO papercrate_app;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA tenant GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO papercrate_app;
|
||||
END
|
||||
$$;
|
||||
@@ -0,0 +1,51 @@
|
||||
DROP POLICY IF EXISTS tenant_membership_select_policy ON tenant.user_memberships;
|
||||
ALTER TABLE tenant.user_memberships NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.user_memberships DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.webdav_tokens;
|
||||
ALTER TABLE tenant.webdav_tokens NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.webdav_tokens DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.tags;
|
||||
ALTER TABLE tenant.tags NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.tags DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_refresh_token_policy ON tenant.refresh_tokens;
|
||||
ALTER TABLE tenant.refresh_tokens NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.refresh_tokens DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.folders;
|
||||
ALTER TABLE tenant.folders NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.folders DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.documents;
|
||||
ALTER TABLE tenant.documents NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.documents DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.document_versions;
|
||||
ALTER TABLE tenant.document_versions NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.document_versions DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.document_tags;
|
||||
ALTER TABLE tenant.document_tags NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.document_tags DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.document_correspondents;
|
||||
ALTER TABLE tenant.document_correspondents NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.document_correspondents DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.document_assets;
|
||||
ALTER TABLE tenant.document_assets NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.document_assets DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.document_asset_objects;
|
||||
ALTER TABLE tenant.document_asset_objects NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.document_asset_objects DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS tenant_isolation_policy ON tenant.correspondents;
|
||||
ALTER TABLE tenant.correspondents NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.correspondents DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP FUNCTION IF EXISTS shared.current_refresh_token_hash();
|
||||
DROP FUNCTION IF EXISTS shared.current_user_id();
|
||||
DROP FUNCTION IF EXISTS shared.current_tenant_id();
|
||||
@@ -0,0 +1,69 @@
|
||||
CREATE OR REPLACE FUNCTION shared.current_tenant_id() RETURNS uuid AS $$
|
||||
SELECT CASE
|
||||
WHEN setting IS NULL OR setting = '' THEN NULL
|
||||
ELSE setting::uuid
|
||||
END
|
||||
FROM (SELECT current_setting('papercrate.tenant_id', true) AS setting) s;
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION shared.current_user_id() RETURNS uuid AS $$
|
||||
SELECT CASE
|
||||
WHEN setting IS NULL OR setting = '' THEN NULL
|
||||
ELSE setting::uuid
|
||||
END
|
||||
FROM (SELECT current_setting('papercrate.user_id', true) AS setting) s;
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION shared.current_refresh_token_hash() RETURNS text AS $$
|
||||
SELECT NULLIF(current_setting('papercrate.refresh_token_hash', true), '')
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
-- Helper to create tenant isolation policy
|
||||
CREATE OR REPLACE FUNCTION shared.ensure_tenant_policy(table_reg regclass) RETURNS void AS $$
|
||||
BEGIN
|
||||
EXECUTE format('ALTER TABLE %s ENABLE ROW LEVEL SECURITY', table_reg);
|
||||
EXECUTE format('ALTER TABLE %s FORCE ROW LEVEL SECURITY', table_reg);
|
||||
EXECUTE format(
|
||||
'CREATE POLICY tenant_isolation_policy ON %s USING (tenant_id = shared.current_tenant_id()) WITH CHECK (tenant_id = shared.current_tenant_id())',
|
||||
table_reg
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
SELECT shared.ensure_tenant_policy('tenant.correspondents');
|
||||
SELECT shared.ensure_tenant_policy('tenant.document_asset_objects');
|
||||
SELECT shared.ensure_tenant_policy('tenant.document_assets');
|
||||
SELECT shared.ensure_tenant_policy('tenant.document_correspondents');
|
||||
SELECT shared.ensure_tenant_policy('tenant.document_tags');
|
||||
SELECT shared.ensure_tenant_policy('tenant.document_versions');
|
||||
SELECT shared.ensure_tenant_policy('tenant.documents');
|
||||
SELECT shared.ensure_tenant_policy('tenant.folders');
|
||||
SELECT shared.ensure_tenant_policy('tenant.tags');
|
||||
SELECT shared.ensure_tenant_policy('tenant.webdav_tokens');
|
||||
|
||||
-- user_memberships has a special read policy to allow tenant discovery during login
|
||||
ALTER TABLE tenant.user_memberships ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.user_memberships FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_membership_select_policy ON tenant.user_memberships
|
||||
USING (
|
||||
tenant_id = shared.current_tenant_id()
|
||||
OR (
|
||||
shared.current_user_id() IS NOT NULL
|
||||
AND user_id = shared.current_user_id()
|
||||
)
|
||||
)
|
||||
WITH CHECK (tenant_id = shared.current_tenant_id());
|
||||
|
||||
ALTER TABLE tenant.refresh_tokens ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tenant.refresh_tokens FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_refresh_token_policy ON tenant.refresh_tokens
|
||||
USING (
|
||||
tenant_id = shared.current_tenant_id()
|
||||
OR (
|
||||
shared.current_refresh_token_hash() IS NOT NULL
|
||||
AND token_hash = shared.current_refresh_token_hash()
|
||||
)
|
||||
)
|
||||
WITH CHECK (tenant_id = shared.current_tenant_id());
|
||||
|
||||
DROP FUNCTION shared.ensure_tenant_policy(regclass);
|
||||
Reference in New Issue
Block a user