Files
archivdms/internal/storage/migrations/008_permissions.sql
T
patrick 9a24ea29e1 FDN-01: repository & projektgerüst
Git-Repository für bestehenden archivdms-Code initialisiert, Branch-/Commit-Konvention (feature/<ticket>-<slug>-Branches, Ticket-Prefix in Commit-Nachricht) etabliert.
2026-08-11 21:27:53 +02:00

79 lines
3.8 KiB
SQL

-- PROJ: permission-model-group-acl
-- Doku-only (siehe README.md in diesem Verzeichnis) — die tatsächliche
-- Ausführung passiert idempotent über internal/storage/permissions.go
-- initPermissionsSchema(), aufgerufen aus (*Store).New().
--
-- Berechtigungsmodell (gruppenaufgelöste, geschichtete Dokument-ACL):
-- Zugriff wird NIE direkt pro User vergeben, sondern über permission_groups.
-- Auflösung über drei Ebenen, spezifischer schlägt allgemeiner:
-- 1. document_grants (pro Dokument, 'deny' entfernt eine Gruppe komplett)
-- 2. tag_grants (über die Tags des Dokuments)
-- 3. document_type_grants (über documents.doc_type_id)
-- Ergebnis wird von RecomputeVisibility() nach document_visibility
-- materialisiert (DELETE+INSERT je Dokument in einer Transaktion).
--
-- Rollen bleiben Außengrenze: superadmin sieht alles tenant-übergreifend,
-- domain_admin sieht per Default alles im eigenen Tenant (bypasst ACL), nur
-- Rolle 'user' wird in ListDocuments gegen document_visibility gefiltert.
--
-- RecomputeVisibility MUSS neu laufen, wenn sich Grants, die Tags eines
-- Dokuments (AttachTag/DetachTag) oder der Dokumenttyp (SetDocumentDocType)
-- ändern — diese Aufrufe sind im Go-Code angehängt.
-- Hinweis: tenant_id / user_id / granted_by tragen bewusst KEINE FK auf
-- tenants(id) bzw. users(id) — konsistent mit documents/taxonomy (plain BIGINT
-- tenant_id) und weil tenants/users von anderen Stores NACH storage.New()
-- angelegt werden (Reihenfolge in cmd/archivdms/main.go). Tenant-Ownership wird
-- applikationsseitig geprüft (permissions.go).
CREATE TABLE IF NOT EXISTS permission_groups (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (tenant_id, name)
);
CREATE TABLE IF NOT EXISTS permission_group_members (
group_id BIGINT NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
user_id BIGINT NOT NULL,
PRIMARY KEY (group_id, user_id)
);
CREATE TABLE IF NOT EXISTS document_type_grants (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
doc_type_id BIGINT NOT NULL REFERENCES document_types(id) ON DELETE CASCADE,
group_id BIGINT NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
access TEXT NOT NULL DEFAULT 'read' CHECK (access IN ('read','write')),
UNIQUE (doc_type_id, group_id)
);
CREATE TABLE IF NOT EXISTS tag_grants (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
tag_id BIGINT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
group_id BIGINT NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
access TEXT NOT NULL DEFAULT 'read' CHECK (access IN ('read','write')),
UNIQUE (tag_id, group_id)
);
CREATE TABLE IF NOT EXISTS document_grants (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
group_id BIGINT NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
access TEXT NOT NULL CHECK (access IN ('read','write','deny')),
granted_by BIGINT NOT NULL,
granted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (document_id, group_id)
);
CREATE TABLE IF NOT EXISTS document_visibility (
document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
group_id BIGINT NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
access TEXT NOT NULL CHECK (access IN ('read','write')),
PRIMARY KEY (document_id, group_id)
);
CREATE INDEX IF NOT EXISTS idx_doc_visibility_group ON document_visibility(group_id, document_id);
CREATE INDEX IF NOT EXISTS idx_pgm_user ON permission_group_members(user_id, group_id);