feat(t-paliad-148) commit 1/6: migration 057 — schema + backfill + user_project_authority_level
Adds paliad.users.profession (firm-wide career tier) and paliad.project_teams.responsibility
(per-project responsibility, default 'member'). Backfills both from the legacy
project_teams.role column — highest-tier-per-user for profession, single-row map
for responsibility (lead→lead, observer→observer, local_counsel/expert→external,
others→member).
Updates paliad.approval_role_level to recognise 'partner' as the new ceiling
(replaces 'lead' as the firm-tier ceiling), keeping 'lead' at level 5 as a
deprecated-shadow row until follow-up migration 058 retires project_teams.role.
Updates paliad.approval_role_from_unit_role: lead → partner.
Creates paliad.user_project_authority_level(user_id, project_id) — the
tuple-with-gate ladder. Returns profession_level if responsibility ∈ {lead,member}
else 0; max with derived authority via partner-unit attachments where
derive_grants_authority=true.
Updates approval_policies.required_role + approval_requests.required_role CHECK
constraints (drop 'lead', add 'partner'); backfills any existing rows.
Rewrites project_partner_units write RLS policy to read pt.responsibility='lead'
instead of pt.role='lead'.
Live-DB BEGIN/ROLLBACK dry-run verified: 2 users get profession='partner'
(matthias.siebels, tester@hlc.de — the only users currently on project_teams),
45 users get profession=NULL (admin fills via /admin/team).
project_teams.role kept as deprecated shadow column. Drop in follow-up migration 058.
This commit is contained in:
124
internal/db/migrations/057_profession_vs_responsibility.down.sql
Normal file
124
internal/db/migrations/057_profession_vs_responsibility.down.sql
Normal file
@@ -0,0 +1,124 @@
|
||||
-- Reverse of 057_profession_vs_responsibility.up.sql.
|
||||
--
|
||||
-- Best-effort rollback. The new columns are dropped; the legacy
|
||||
-- project_teams.role column is re-derived from (responsibility, profession).
|
||||
-- Down-migration loses information on edges:
|
||||
-- * external responsibility → role='local_counsel' (loses expert distinction)
|
||||
-- * member + profession=partner → role='of_counsel' (no legacy 'partner'
|
||||
-- existed in project_teams.role; closest legacy ceiling)
|
||||
-- * member + profession=paralegal → role='pa' (no legacy paralegal)
|
||||
-- * member + profession=NULL → role='associate' (safe default, matches
|
||||
-- the legacy RoleAssociate default)
|
||||
-- These edges are documented; if the down is run on real production data,
|
||||
-- review per-row before commit.
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. Restore approval_role_level to point at legacy ladder values.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION paliad.approval_role_level(role text)
|
||||
RETURNS int LANGUAGE SQL IMMUTABLE AS $$
|
||||
SELECT CASE role
|
||||
WHEN 'lead' THEN 5
|
||||
WHEN 'of_counsel' THEN 4
|
||||
WHEN 'associate' THEN 3
|
||||
WHEN 'senior_pa' THEN 2
|
||||
WHEN 'pa' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. Restore approval_role_from_unit_role lead → lead.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION paliad.approval_role_from_unit_role(unit_role text)
|
||||
RETURNS text LANGUAGE SQL IMMUTABLE AS $$
|
||||
SELECT CASE unit_role
|
||||
WHEN 'lead' THEN 'lead'
|
||||
WHEN 'attorney' THEN 'associate'
|
||||
WHEN 'senior_pa' THEN 'senior_pa'
|
||||
WHEN 'pa' THEN 'pa'
|
||||
ELSE 'observer'
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. Re-derive project_teams.role from (responsibility, profession).
|
||||
-- ============================================================================
|
||||
|
||||
UPDATE paliad.project_teams pt
|
||||
SET role = CASE
|
||||
WHEN pt.responsibility = 'lead' THEN 'lead'
|
||||
WHEN pt.responsibility = 'observer' THEN 'observer'
|
||||
WHEN pt.responsibility = 'external' THEN 'local_counsel'
|
||||
ELSE COALESCE(
|
||||
(SELECT CASE u.profession
|
||||
WHEN 'partner' THEN 'of_counsel' -- best-effort: no legacy 'partner' role
|
||||
WHEN 'of_counsel' THEN 'of_counsel'
|
||||
WHEN 'associate' THEN 'associate'
|
||||
WHEN 'senior_pa' THEN 'senior_pa'
|
||||
WHEN 'pa' THEN 'pa'
|
||||
WHEN 'paralegal' THEN 'pa' -- closest legacy fit
|
||||
END
|
||||
FROM paliad.users u WHERE u.id = pt.user_id),
|
||||
'associate'
|
||||
)
|
||||
END;
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. Restore approval_policies + approval_requests CHECK constraints.
|
||||
-- ============================================================================
|
||||
|
||||
UPDATE paliad.approval_policies
|
||||
SET required_role = 'lead'
|
||||
WHERE required_role = 'partner';
|
||||
|
||||
ALTER TABLE paliad.approval_policies DROP CONSTRAINT IF EXISTS approval_policies_required_role_check;
|
||||
ALTER TABLE paliad.approval_policies ADD CONSTRAINT approval_policies_required_role_check
|
||||
CHECK (required_role IN ('lead', 'of_counsel', 'associate', 'senior_pa', 'pa'));
|
||||
|
||||
UPDATE paliad.approval_requests
|
||||
SET required_role = 'lead'
|
||||
WHERE required_role = 'partner';
|
||||
|
||||
ALTER TABLE paliad.approval_requests DROP CONSTRAINT IF EXISTS approval_requests_required_role_check;
|
||||
ALTER TABLE paliad.approval_requests ADD CONSTRAINT approval_requests_required_role_check
|
||||
CHECK (required_role IN ('lead', 'of_counsel', 'associate', 'senior_pa', 'pa'));
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. Restore project_partner_units RLS to read pt.role = 'lead'.
|
||||
-- ============================================================================
|
||||
|
||||
DROP POLICY IF EXISTS project_partner_units_write ON paliad.project_partner_units;
|
||||
|
||||
CREATE POLICY project_partner_units_write
|
||||
ON paliad.project_partner_units FOR ALL
|
||||
USING (
|
||||
EXISTS (SELECT 1 FROM paliad.users u
|
||||
WHERE u.id = auth.uid() AND u.global_role = 'global_admin')
|
||||
OR EXISTS (SELECT 1 FROM paliad.project_teams pt
|
||||
WHERE pt.user_id = auth.uid()
|
||||
AND pt.project_id = project_partner_units.project_id
|
||||
AND pt.role = 'lead')
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM paliad.users u
|
||||
WHERE u.id = auth.uid() AND u.global_role = 'global_admin')
|
||||
OR EXISTS (SELECT 1 FROM paliad.project_teams pt
|
||||
WHERE pt.user_id = auth.uid()
|
||||
AND pt.project_id = project_partner_units.project_id
|
||||
AND pt.role = 'lead')
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- 6. Drop the new function and columns.
|
||||
-- ============================================================================
|
||||
|
||||
DROP FUNCTION IF EXISTS paliad.user_project_authority_level(uuid, uuid);
|
||||
|
||||
DROP INDEX IF EXISTS paliad.project_teams_responsibility_idx;
|
||||
ALTER TABLE paliad.project_teams DROP COLUMN IF EXISTS responsibility;
|
||||
|
||||
DROP INDEX IF EXISTS paliad.users_profession_idx;
|
||||
ALTER TABLE paliad.users DROP COLUMN IF EXISTS profession;
|
||||
339
internal/db/migrations/057_profession_vs_responsibility.up.sql
Normal file
339
internal/db/migrations/057_profession_vs_responsibility.up.sql
Normal file
@@ -0,0 +1,339 @@
|
||||
-- t-paliad-148: split paliad.project_teams.role into firm-level profession
|
||||
-- and project-level responsibility.
|
||||
--
|
||||
-- Design: docs/design-profession-vs-project-role-2026-05-07.md (kepler,
|
||||
-- m-locked 2026-05-07 21:35).
|
||||
--
|
||||
-- The legacy column did two jobs at once:
|
||||
-- - career tier at the firm (PA, Associate, Of Counsel, …)
|
||||
-- - responsibility on this matter (Lead, Member, Observer)
|
||||
-- This migration introduces two clean axes and backfills both from the
|
||||
-- legacy column. The legacy column is kept as a deprecated shadow for one
|
||||
-- release; a follow-up migration drops it after Go code has fully
|
||||
-- migrated and the production data is verified clean.
|
||||
--
|
||||
-- Day-1 deploy = zero behaviour change because the new code paths read
|
||||
-- the new columns and the backfill is run inside this migration.
|
||||
--
|
||||
-- Sections:
|
||||
-- 1. ALTER paliad.users ADD COLUMN profession.
|
||||
-- 2. ALTER paliad.project_teams ADD COLUMN responsibility.
|
||||
-- 3. Backfill profession from highest legacy project_teams.role per user.
|
||||
-- 4. Backfill responsibility from legacy project_teams.role.
|
||||
-- 5. UPDATE paliad.approval_policies.required_role CHECK + 'lead' → 'partner'.
|
||||
-- 6. UPDATE paliad.approval_requests.required_role CHECK + 'lead' → 'partner'.
|
||||
-- 7. UPDATE paliad.approval_role_from_unit_role: lead → partner.
|
||||
-- 8. CREATE paliad.user_project_authority_level — tuple-with-gate ladder.
|
||||
-- 9. CASCADE-rebuild paliad.project_partner_units RLS policies that
|
||||
-- reference pt.role = 'lead'.
|
||||
-- 10. UPDATE COMMENT on paliad.approval_role_level pointing at users.profession.
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. paliad.users.profession — firm-wide career tier.
|
||||
--
|
||||
-- NULL means "no firm tier" (external local counsel, expert, admin
|
||||
-- accounts that aren't practicing lawyers). NULL → ladder level 0 →
|
||||
-- ineligible to approve. Required-on-invite for firm members; admin
|
||||
-- editable on /admin/team.
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE paliad.users
|
||||
ADD COLUMN profession text NULL
|
||||
CHECK (profession IS NULL OR profession IN (
|
||||
'partner', 'of_counsel', 'associate',
|
||||
'senior_pa', 'pa', 'paralegal'
|
||||
));
|
||||
|
||||
CREATE INDEX users_profession_idx ON paliad.users (profession);
|
||||
|
||||
COMMENT ON COLUMN paliad.users.profession IS
|
||||
'Firm-wide career tier driving the t-paliad-138 approval ladder. '
|
||||
'NULL = no firm tier (external collaborators, admin accounts). '
|
||||
'Distinct from job_title (free-text display) and global_role (tool admin gate).';
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. paliad.project_teams.responsibility — per-project responsibility.
|
||||
--
|
||||
-- Replaces the project-axis values that were mixed into project_teams.role.
|
||||
-- Default 'member'. 'lead' has additional manage-project privileges (already
|
||||
-- wired in derivation_service.go). 'observer' and 'external' close the
|
||||
-- approval gate (level 0 regardless of profession).
|
||||
--
|
||||
-- The legacy `role` column is kept on the table as a deprecated shadow for
|
||||
-- one release. New code reads .responsibility; old code paths that still
|
||||
-- read .role continue working until the follow-up migration drops the
|
||||
-- column.
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE paliad.project_teams
|
||||
ADD COLUMN responsibility text NOT NULL DEFAULT 'member'
|
||||
CHECK (responsibility IN ('lead', 'member', 'observer', 'external'));
|
||||
|
||||
CREATE INDEX project_teams_responsibility_idx
|
||||
ON paliad.project_teams (project_id, responsibility);
|
||||
|
||||
COMMENT ON COLUMN paliad.project_teams.responsibility IS
|
||||
'Per-project responsibility on this matter. lead/member open the '
|
||||
'approval gate; observer/external close it. Profession provides the '
|
||||
'level (paliad.users.profession).';
|
||||
|
||||
COMMENT ON COLUMN paliad.project_teams.role IS
|
||||
'DEPRECATED — split into users.profession + project_teams.responsibility '
|
||||
'in migration 057 (t-paliad-148). Kept as a shadow column for one release. '
|
||||
'Drop in follow-up migration 058.';
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. Backfill paliad.users.profession from highest legacy tier per user.
|
||||
--
|
||||
-- Mapping (legacy role → profession):
|
||||
-- lead → partner
|
||||
-- of_counsel → of_counsel
|
||||
-- associate → associate
|
||||
-- senior_pa → senior_pa
|
||||
-- pa → pa
|
||||
-- local_counsel/expert/observer → IGNORED (no firm tier inferable)
|
||||
--
|
||||
-- For each user with at least one project_teams row carrying a firm-tier
|
||||
-- value, take the HIGHEST tier (per the t-138 ladder). Ties at same tier
|
||||
-- collapse trivially (same value). Users with only project-only labels
|
||||
-- (observer / local_counsel / expert) get profession=NULL — admin will
|
||||
-- need to fill them in via /admin/team.
|
||||
-- ============================================================================
|
||||
|
||||
WITH legacy_to_profession AS (
|
||||
SELECT pt.user_id,
|
||||
CASE pt.role
|
||||
WHEN 'lead' THEN 'partner'
|
||||
WHEN 'of_counsel' THEN 'of_counsel'
|
||||
WHEN 'associate' THEN 'associate'
|
||||
WHEN 'senior_pa' THEN 'senior_pa'
|
||||
WHEN 'pa' THEN 'pa'
|
||||
-- observer / local_counsel / expert → NULL (filtered below)
|
||||
END AS profession,
|
||||
CASE pt.role
|
||||
WHEN 'lead' THEN 5
|
||||
WHEN 'of_counsel' THEN 4
|
||||
WHEN 'associate' THEN 3
|
||||
WHEN 'senior_pa' THEN 2
|
||||
WHEN 'pa' THEN 1
|
||||
ELSE 0
|
||||
END AS lvl
|
||||
FROM paliad.project_teams pt
|
||||
),
|
||||
ranked AS (
|
||||
SELECT user_id, profession,
|
||||
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY lvl DESC) AS rn
|
||||
FROM legacy_to_profession
|
||||
WHERE profession IS NOT NULL
|
||||
)
|
||||
UPDATE paliad.users u
|
||||
SET profession = r.profession
|
||||
FROM ranked r
|
||||
WHERE u.id = r.user_id
|
||||
AND r.rn = 1
|
||||
AND u.profession IS NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. Backfill paliad.project_teams.responsibility from legacy role.
|
||||
--
|
||||
-- Per-row mapping:
|
||||
-- lead → lead
|
||||
-- observer → observer
|
||||
-- local_counsel → external
|
||||
-- expert → external
|
||||
-- associate / pa / of_counsel / senior_pa → member
|
||||
--
|
||||
-- Authority for "member" rows now comes from the user's profession.
|
||||
-- ============================================================================
|
||||
|
||||
UPDATE paliad.project_teams
|
||||
SET responsibility = CASE role
|
||||
WHEN 'lead' THEN 'lead'
|
||||
WHEN 'observer' THEN 'observer'
|
||||
WHEN 'local_counsel' THEN 'external'
|
||||
WHEN 'expert' THEN 'external'
|
||||
ELSE 'member'
|
||||
END;
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. paliad.approval_policies.required_role — drop 'lead', add 'partner'.
|
||||
--
|
||||
-- Legacy 'lead' was the project-level value at the ladder ceiling; under the
|
||||
-- new model the ceiling is profession='partner'. Backfill any existing
|
||||
-- policy rows from 'lead' to 'partner', then tighten the CHECK.
|
||||
-- ============================================================================
|
||||
|
||||
UPDATE paliad.approval_policies
|
||||
SET required_role = 'partner'
|
||||
WHERE required_role = 'lead';
|
||||
|
||||
ALTER TABLE paliad.approval_policies DROP CONSTRAINT IF EXISTS approval_policies_required_role_check;
|
||||
ALTER TABLE paliad.approval_policies ADD CONSTRAINT approval_policies_required_role_check
|
||||
CHECK (required_role IN ('partner', 'of_counsel', 'associate', 'senior_pa', 'pa'));
|
||||
|
||||
-- ============================================================================
|
||||
-- 6. paliad.approval_requests.required_role — same rename for snapshots.
|
||||
--
|
||||
-- Each request snapshots the policy's required_role at submission time so
|
||||
-- mid-flight policy edits don't change the bar. Backfill 'lead' → 'partner'
|
||||
-- for parity with the new policy enum, then tighten the CHECK.
|
||||
-- ============================================================================
|
||||
|
||||
UPDATE paliad.approval_requests
|
||||
SET required_role = 'partner'
|
||||
WHERE required_role = 'lead';
|
||||
|
||||
ALTER TABLE paliad.approval_requests DROP CONSTRAINT IF EXISTS approval_requests_required_role_check;
|
||||
ALTER TABLE paliad.approval_requests ADD CONSTRAINT approval_requests_required_role_check
|
||||
CHECK (required_role IN ('partner', 'of_counsel', 'associate', 'senior_pa', 'pa'));
|
||||
|
||||
-- ============================================================================
|
||||
-- 7. paliad.approval_role_from_unit_role — bridge maps lead → partner now.
|
||||
--
|
||||
-- Derived authority via partner-unit attachments (t-paliad-139) bridges
|
||||
-- unit_role to the project-tier ladder. Under the new ladder, the highest
|
||||
-- tier is 'partner' (was 'lead'). Update the lead → lead row to lead →
|
||||
-- partner; the rest of the bridge mapping stays unchanged.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION paliad.approval_role_from_unit_role(unit_role text)
|
||||
RETURNS text LANGUAGE SQL IMMUTABLE AS $$
|
||||
SELECT CASE unit_role
|
||||
WHEN 'lead' THEN 'partner'
|
||||
WHEN 'attorney' THEN 'associate'
|
||||
WHEN 'senior_pa' THEN 'senior_pa'
|
||||
WHEN 'pa' THEN 'pa'
|
||||
ELSE 'observer'
|
||||
END
|
||||
$$;
|
||||
|
||||
-- Update the level helper too: 'partner' replaces 'lead' as the ceiling
|
||||
-- value that the function recognises. The numeric ladder is identical;
|
||||
-- only the named tier shifts.
|
||||
|
||||
CREATE OR REPLACE FUNCTION paliad.approval_role_level(role text)
|
||||
RETURNS int LANGUAGE SQL IMMUTABLE AS $$
|
||||
SELECT CASE role
|
||||
WHEN 'partner' THEN 5
|
||||
WHEN 'of_counsel' THEN 4
|
||||
WHEN 'associate' THEN 3
|
||||
WHEN 'senior_pa' THEN 2
|
||||
WHEN 'pa' THEN 1
|
||||
WHEN 'paralegal' THEN 0
|
||||
-- Legacy 'lead' kept at level 5 for the deprecated-shadow window:
|
||||
-- old call sites that still read pt.role would otherwise return
|
||||
-- level 0 and break authority for projects where the migration
|
||||
-- has run but the Go redirect hasn't. Removed in migration 058.
|
||||
WHEN 'lead' THEN 5
|
||||
ELSE 0
|
||||
END
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION paliad.approval_role_level(text) IS
|
||||
'Strict-ladder level for the t-paliad-138 / t-paliad-148 approval gate. '
|
||||
'Reads paliad.users.profession; legacy project_teams.role values still '
|
||||
'recognised via the lead→5 shadow row until migration 058 retires the '
|
||||
'column. Higher level always satisfies lower; level 0 = ineligible.';
|
||||
|
||||
-- ============================================================================
|
||||
-- 8. paliad.user_project_authority_level — tuple-with-gate ladder.
|
||||
--
|
||||
-- effective_level for user U on project P:
|
||||
--
|
||||
-- profession_level = approval_role_level(U.profession) -- 0 if NULL
|
||||
-- responsibility = direct or ancestor on project P
|
||||
-- gate_open = responsibility IN ('lead', 'member')
|
||||
-- derived_role = approval_role_from_unit_role(unit_role)
|
||||
-- when project_partner_units.derive_grants_authority
|
||||
-- effective_level = MAX over sources, gated as above
|
||||
--
|
||||
-- Direct/ancestor responsibility opens the gate, profession provides the
|
||||
-- level. Derivation is its own source — derived authority always opens
|
||||
-- its own gate (the unit attachment's grants_authority flag is the gate).
|
||||
-- A user can hit this function via direct membership AND derivation; the
|
||||
-- result is the max of both sources.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION paliad.user_project_authority_level(
|
||||
_user_id uuid,
|
||||
_project_id uuid
|
||||
) RETURNS int
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
AS $$
|
||||
WITH path AS (
|
||||
SELECT string_to_array(p.path, '.')::uuid[] AS ids
|
||||
FROM paliad.projects p WHERE p.id = _project_id
|
||||
),
|
||||
direct_or_ancestor AS (
|
||||
SELECT pt.responsibility
|
||||
FROM paliad.project_teams pt
|
||||
JOIN path ON pt.project_id = ANY(path.ids)
|
||||
WHERE pt.user_id = _user_id
|
||||
),
|
||||
profession_level AS (
|
||||
SELECT paliad.approval_role_level(u.profession) AS lvl
|
||||
FROM paliad.users u WHERE u.id = _user_id
|
||||
),
|
||||
direct_level AS (
|
||||
-- Profession-level if any membership row opens the gate, else 0.
|
||||
SELECT CASE
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM direct_or_ancestor doa
|
||||
WHERE doa.responsibility IN ('lead', 'member')
|
||||
) THEN COALESCE((SELECT lvl FROM profession_level), 0)
|
||||
ELSE 0
|
||||
END AS lvl
|
||||
),
|
||||
derived_level AS (
|
||||
SELECT COALESCE(MAX(paliad.approval_role_level(
|
||||
paliad.approval_role_from_unit_role(pum.unit_role)
|
||||
)), 0) AS lvl
|
||||
FROM paliad.project_partner_units ppu
|
||||
JOIN paliad.partner_unit_members pum
|
||||
ON pum.partner_unit_id = ppu.partner_unit_id
|
||||
AND pum.user_id = _user_id
|
||||
AND pum.unit_role = ANY(ppu.derive_unit_roles)
|
||||
JOIN path ON ppu.project_id = ANY(path.ids)
|
||||
WHERE ppu.derive_grants_authority = true
|
||||
)
|
||||
SELECT GREATEST(
|
||||
(SELECT lvl FROM direct_level),
|
||||
(SELECT lvl FROM derived_level)
|
||||
);
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION paliad.user_project_authority_level(uuid, uuid) IS
|
||||
'Effective approval-ladder level for user U on project P, evaluated as '
|
||||
'a tuple-with-gate: profession_level if responsibility ∈ {lead,member} '
|
||||
'else 0; max with derived authority (partner-unit attachment with '
|
||||
'grants_authority=true). t-paliad-148.';
|
||||
|
||||
-- ============================================================================
|
||||
-- 9. paliad.project_partner_units RLS — switch lead-gate to .responsibility.
|
||||
--
|
||||
-- Migration 055 wrote two policies that gate writes on pt.role = 'lead'.
|
||||
-- Under the new model, lead is a project responsibility, not a profession.
|
||||
-- Drop and rewrite both policies to read .responsibility.
|
||||
-- ============================================================================
|
||||
|
||||
DROP POLICY IF EXISTS project_partner_units_write ON paliad.project_partner_units;
|
||||
|
||||
CREATE POLICY project_partner_units_write
|
||||
ON paliad.project_partner_units FOR ALL
|
||||
USING (
|
||||
EXISTS (SELECT 1 FROM paliad.users u
|
||||
WHERE u.id = auth.uid() AND u.global_role = 'global_admin')
|
||||
OR EXISTS (SELECT 1 FROM paliad.project_teams pt
|
||||
WHERE pt.user_id = auth.uid()
|
||||
AND pt.project_id = project_partner_units.project_id
|
||||
AND pt.responsibility = 'lead')
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM paliad.users u
|
||||
WHERE u.id = auth.uid() AND u.global_role = 'global_admin')
|
||||
OR EXISTS (SELECT 1 FROM paliad.project_teams pt
|
||||
WHERE pt.user_id = auth.uid()
|
||||
AND pt.project_id = project_partner_units.project_id
|
||||
AND pt.responsibility = 'lead')
|
||||
);
|
||||
Reference in New Issue
Block a user