Files
dashboard/backend/src/database/migrations/037_create_permission_system.sql
Matthias Hochmeister fa10467f21 rights system
2026-03-23 12:12:21 +01:00

441 lines
22 KiB
SQL

-- Migration 037: DB-driven permission system
-- Replaces hardcoded RBAC with per-Authentik-group permission assignments
-- DROP + recreate is safe because this feature has not been deployed yet.
-- ═══════════════════════════════════════════════════════════════════════════
-- 0. Clean slate
-- ═══════════════════════════════════════════════════════════════════════════
DROP TABLE IF EXISTS group_permissions CASCADE;
DROP TABLE IF EXISTS permissions CASCADE;
DROP TABLE IF EXISTS feature_groups CASCADE;
-- ═══════════════════════════════════════════════════════════════════════════
-- 1. Feature Groups
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE feature_groups (
id VARCHAR(50) PRIMARY KEY,
label VARCHAR(100) NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
maintenance BOOLEAN NOT NULL DEFAULT FALSE
);
INSERT INTO feature_groups (id, label, sort_order) VALUES
('kalender', 'Kalender', 1),
('fahrzeuge', 'Fahrzeuge', 2),
('einsaetze', 'Einsätze', 3),
('ausruestung', 'Ausrüstung', 4),
('mitglieder', 'Mitglieder', 5),
('atemschutz', 'Atemschutz', 6),
('wissen', 'Wissen', 7),
('vikunja', 'Vikunja', 8),
('dashboard', 'Dashboard', 9),
('admin', 'Admin', 10)
ON CONFLICT (id) DO NOTHING;
-- ═══════════════════════════════════════════════════════════════════════════
-- 2. Permissions
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE permissions (
id VARCHAR(100) PRIMARY KEY,
feature_group_id VARCHAR(50) NOT NULL REFERENCES feature_groups(id) ON DELETE CASCADE,
label VARCHAR(150) NOT NULL,
description TEXT,
sort_order INT NOT NULL DEFAULT 0
);
-- Kalender permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('kalender:view', 'kalender', 'Ansehen', 'Kalender einsehen (Termine, Übungen, Buchungen)', 1),
('kalender:create', 'kalender', 'Erstellen', 'Termine und Übungen erstellen', 2),
('kalender:cancel', 'kalender', 'Absagen', 'Termine und Übungen absagen', 3),
('kalender:mark_attendance', 'kalender', 'Anwesenheit eintragen', 'Teilnahme bestätigen', 4),
('kalender:create_bookings', 'kalender', 'Buchungen erstellen', 'Neue Fahrzeugbuchungen anlegen', 5),
('kalender:edit_bookings', 'kalender', 'Buchungen bearbeiten', 'Bestehende Buchungen ändern', 6),
('kalender:cancel_own_bookings','kalender', 'Eigene Buchungen stornieren','Eigene Buchungen stornieren', 7),
('kalender:delete_bookings', 'kalender', 'Alle Buchungen stornieren/löschen', 'Alle Buchungen stornieren oder löschen', 8),
('kalender:manage_categories', 'kalender', 'Kategorien verwalten', 'Veranstaltungskategorien verwalten', 9),
('kalender:view_reports', 'kalender', 'Berichte ansehen', 'Übungsstatistiken und Berichte einsehen', 10),
('kalender:widget_events', 'kalender', 'Widget: Termine', 'Dashboard-Widget für Termine', 11),
('kalender:widget_bookings', 'kalender', 'Widget: Buchungen', 'Dashboard-Widget für Buchungen', 12),
('kalender:widget_quick_add', 'kalender', 'Widget: Schnell-Termin', 'Dashboard-Widget zum schnellen Erstellen', 13)
ON CONFLICT (id) DO NOTHING;
-- Fahrzeuge permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('fahrzeuge:view', 'fahrzeuge', 'Ansehen', 'Fahrzeugdetails einsehen', 1),
('fahrzeuge:create', 'fahrzeuge', 'Erstellen', 'Neue Fahrzeuge anlegen', 2),
('fahrzeuge:change_status', 'fahrzeuge', 'Status ändern', 'Fahrzeugstatus ändern', 3),
('fahrzeuge:manage_maintenance', 'fahrzeuge', 'Wartung verwalten', 'Wartungseinträge erstellen/bearbeiten', 4),
('fahrzeuge:delete', 'fahrzeuge', 'Löschen', 'Fahrzeuge löschen', 5),
('fahrzeuge:widget', 'fahrzeuge', 'Widget', 'Dashboard-Widget für Fahrzeuge', 6)
ON CONFLICT (id) DO NOTHING;
-- Einsätze permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('einsaetze:view', 'einsaetze', 'Ansehen', 'Einsätze einsehen', 1),
('einsaetze:view_reports', 'einsaetze', 'Berichte ansehen', 'Einsatzberichte/Berichtstext einsehen', 2),
('einsaetze:create', 'einsaetze', 'Erstellen', 'Neue Einsätze anlegen und bearbeiten', 3),
('einsaetze:delete', 'einsaetze', 'Löschen', 'Einsätze archivieren/löschen', 4),
('einsaetze:manage_personnel', 'einsaetze', 'Personal verwalten', 'Einsatzpersonal und Fahrzeuge zuweisen', 5)
ON CONFLICT (id) DO NOTHING;
-- Ausrüstung permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('ausruestung:view', 'ausruestung', 'Ansehen', 'Ausrüstung einsehen', 1),
('ausruestung:create', 'ausruestung', 'Erstellen', 'Neue Ausrüstung anlegen und bearbeiten', 2),
('ausruestung:manage_maintenance', 'ausruestung', 'Wartung verwalten', 'Wartungseinträge verwalten', 3),
('ausruestung:delete', 'ausruestung', 'Löschen', 'Ausrüstung löschen', 4),
('ausruestung:widget', 'ausruestung', 'Widget', 'Dashboard-Widget für Ausrüstung', 5)
ON CONFLICT (id) DO NOTHING;
-- Mitglieder permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('mitglieder:view_own', 'mitglieder', 'Eigenes Profil', 'Eigenes Profil einsehen', 1),
('mitglieder:view_all', 'mitglieder', 'Alle Profile', 'Alle Mitglieder-Profile einsehen', 2),
('mitglieder:edit', 'mitglieder', 'Bearbeiten', 'Mitglieder-Profile bearbeiten', 3),
('mitglieder:create_profile','mitglieder', 'Profil erstellen', 'Neue Mitglieder-Profile anlegen', 4)
ON CONFLICT (id) DO NOTHING;
-- Atemschutz permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('atemschutz:view', 'atemschutz', 'Ansehen', 'Atemschutz-Daten aller Träger sehen', 1),
('atemschutz:create', 'atemschutz', 'Erstellen', 'Atemschutz-Einträge anlegen/ändern', 2),
('atemschutz:delete', 'atemschutz', 'Löschen', 'Atemschutz-Einträge löschen', 3),
('atemschutz:widget', 'atemschutz', 'Widget', 'Dashboard-Widget für Atemschutz', 4)
ON CONFLICT (id) DO NOTHING;
-- Wissen permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('wissen:view', 'wissen', 'Ansehen', 'Wissen-Seite anzeigen', 1),
('wissen:widget_recent', 'wissen', 'Widget: Letzte', 'Dashboard-Widget letzte Seiten', 2),
('wissen:widget_search', 'wissen', 'Widget: Suche', 'Dashboard-Widget für BookStack-Suche', 3)
ON CONFLICT (id) DO NOTHING;
-- Vikunja permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('vikunja:create_tasks', 'vikunja', 'Aufgaben erstellen', 'Neue Vikunja-Aufgaben erstellen', 1),
('vikunja:widget_tasks', 'vikunja', 'Widget: Aufgaben', 'Dashboard-Widget für Vikunja-Aufgaben', 2),
('vikunja:widget_quick_add', 'vikunja', 'Widget: Schnell-Task', 'Dashboard-Widget zum schnellen Erstellen', 3)
ON CONFLICT (id) DO NOTHING;
-- Dashboard permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('dashboard:widget_links', 'dashboard', 'Widget: Links', 'Dashboard-Widget für externe Links', 1),
('dashboard:widget_banner', 'dashboard', 'Widget: Banner', 'Dashboard-Widget für Banner', 2)
ON CONFLICT (id) DO NOTHING;
-- Admin permissions
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
('admin:view', 'admin', 'Ansehen', 'Admin-Panel einsehen', 1),
('admin:write', 'admin', 'Bearbeiten', 'Admin-Einstellungen ändern', 2)
ON CONFLICT (id) DO NOTHING;
-- ═══════════════════════════════════════════════════════════════════════════
-- 3. Group Permissions
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE group_permissions (
authentik_group VARCHAR(100) NOT NULL,
permission_id VARCHAR(100) NOT NULL REFERENCES permissions(id) ON DELETE CASCADE,
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
granted_by UUID REFERENCES users(id) ON DELETE SET NULL,
PRIMARY KEY (authentik_group, permission_id)
);
CREATE INDEX idx_group_permissions_group ON group_permissions(authentik_group);
-- ═══════════════════════════════════════════════════════════════════════════
-- 4. Seed data — replicate current RBAC behavior
-- ═══════════════════════════════════════════════════════════════════════════
-- NOTE: dashboard_admin is NOT seeded — it has hardwired full access in code.
-- ── dashboard_kommando — near-full access ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender (ALL)
('dashboard_kommando', 'kalender:view'),
('dashboard_kommando', 'kalender:create'),
('dashboard_kommando', 'kalender:cancel'),
('dashboard_kommando', 'kalender:mark_attendance'),
('dashboard_kommando', 'kalender:create_bookings'),
('dashboard_kommando', 'kalender:edit_bookings'),
('dashboard_kommando', 'kalender:cancel_own_bookings'),
('dashboard_kommando', 'kalender:delete_bookings'),
('dashboard_kommando', 'kalender:manage_categories'),
('dashboard_kommando', 'kalender:view_reports'),
('dashboard_kommando', 'kalender:widget_events'),
('dashboard_kommando', 'kalender:widget_bookings'),
('dashboard_kommando', 'kalender:widget_quick_add'),
-- Fahrzeuge (ALL)
('dashboard_kommando', 'fahrzeuge:view'),
('dashboard_kommando', 'fahrzeuge:create'),
('dashboard_kommando', 'fahrzeuge:change_status'),
('dashboard_kommando', 'fahrzeuge:manage_maintenance'),
('dashboard_kommando', 'fahrzeuge:delete'),
('dashboard_kommando', 'fahrzeuge:widget'),
-- Einsätze (ALL)
('dashboard_kommando', 'einsaetze:view'),
('dashboard_kommando', 'einsaetze:view_reports'),
('dashboard_kommando', 'einsaetze:create'),
('dashboard_kommando', 'einsaetze:delete'),
('dashboard_kommando', 'einsaetze:manage_personnel'),
-- Ausrüstung (ALL)
('dashboard_kommando', 'ausruestung:view'),
('dashboard_kommando', 'ausruestung:create'),
('dashboard_kommando', 'ausruestung:manage_maintenance'),
('dashboard_kommando', 'ausruestung:delete'),
('dashboard_kommando', 'ausruestung:widget'),
-- Mitglieder (ALL)
('dashboard_kommando', 'mitglieder:view_own'),
('dashboard_kommando', 'mitglieder:view_all'),
('dashboard_kommando', 'mitglieder:edit'),
('dashboard_kommando', 'mitglieder:create_profile'),
-- Atemschutz (ALL)
('dashboard_kommando', 'atemschutz:view'),
('dashboard_kommando', 'atemschutz:create'),
('dashboard_kommando', 'atemschutz:delete'),
('dashboard_kommando', 'atemschutz:widget'),
-- Wissen (ALL)
('dashboard_kommando', 'wissen:view'),
('dashboard_kommando', 'wissen:widget_recent'),
('dashboard_kommando', 'wissen:widget_search'),
-- Vikunja (ALL)
('dashboard_kommando', 'vikunja:create_tasks'),
('dashboard_kommando', 'vikunja:widget_tasks'),
('dashboard_kommando', 'vikunja:widget_quick_add'),
-- Dashboard (ALL)
('dashboard_kommando', 'dashboard:widget_links'),
('dashboard_kommando', 'dashboard:widget_banner'),
-- Admin (view only)
('dashboard_kommando', 'admin:view')
ON CONFLICT DO NOTHING;
-- ── dashboard_fahrmeister — vehicle specialist ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender
('dashboard_fahrmeister', 'kalender:view'),
('dashboard_fahrmeister', 'kalender:create_bookings'),
('dashboard_fahrmeister', 'kalender:edit_bookings'),
('dashboard_fahrmeister', 'kalender:cancel_own_bookings'),
('dashboard_fahrmeister', 'kalender:widget_events'),
('dashboard_fahrmeister', 'kalender:widget_bookings'),
-- Fahrzeuge
('dashboard_fahrmeister', 'fahrzeuge:view'),
('dashboard_fahrmeister', 'fahrzeuge:change_status'),
('dashboard_fahrmeister', 'fahrzeuge:manage_maintenance'),
('dashboard_fahrmeister', 'fahrzeuge:widget'),
-- Einsätze
('dashboard_fahrmeister', 'einsaetze:view'),
-- Ausrüstung
('dashboard_fahrmeister', 'ausruestung:view'),
('dashboard_fahrmeister', 'ausruestung:create'),
('dashboard_fahrmeister', 'ausruestung:manage_maintenance'),
('dashboard_fahrmeister', 'ausruestung:widget'),
-- Mitglieder
('dashboard_fahrmeister', 'mitglieder:view_own'),
('dashboard_fahrmeister', 'mitglieder:view_all'),
-- Atemschutz
('dashboard_fahrmeister', 'atemschutz:widget'),
-- Wissen
('dashboard_fahrmeister', 'wissen:view'),
('dashboard_fahrmeister', 'wissen:widget_recent'),
('dashboard_fahrmeister', 'wissen:widget_search'),
-- Vikunja
('dashboard_fahrmeister', 'vikunja:create_tasks'),
('dashboard_fahrmeister', 'vikunja:widget_tasks'),
('dashboard_fahrmeister', 'vikunja:widget_quick_add'),
-- Dashboard
('dashboard_fahrmeister', 'dashboard:widget_links'),
('dashboard_fahrmeister', 'dashboard:widget_banner')
ON CONFLICT DO NOTHING;
-- ── dashboard_zeugmeister — equipment specialist ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender
('dashboard_zeugmeister', 'kalender:view'),
('dashboard_zeugmeister', 'kalender:create_bookings'),
('dashboard_zeugmeister', 'kalender:cancel_own_bookings'),
('dashboard_zeugmeister', 'kalender:widget_events'),
('dashboard_zeugmeister', 'kalender:widget_bookings'),
-- Fahrzeuge
('dashboard_zeugmeister', 'fahrzeuge:view'),
('dashboard_zeugmeister', 'fahrzeuge:change_status'),
('dashboard_zeugmeister', 'fahrzeuge:manage_maintenance'),
('dashboard_zeugmeister', 'fahrzeuge:widget'),
-- Einsätze
('dashboard_zeugmeister', 'einsaetze:view'),
-- Ausrüstung
('dashboard_zeugmeister', 'ausruestung:view'),
('dashboard_zeugmeister', 'ausruestung:create'),
('dashboard_zeugmeister', 'ausruestung:manage_maintenance'),
('dashboard_zeugmeister', 'ausruestung:widget'),
-- Mitglieder
('dashboard_zeugmeister', 'mitglieder:view_own'),
('dashboard_zeugmeister', 'mitglieder:view_all'),
-- Atemschutz
('dashboard_zeugmeister', 'atemschutz:widget'),
-- Wissen
('dashboard_zeugmeister', 'wissen:view'),
('dashboard_zeugmeister', 'wissen:widget_recent'),
('dashboard_zeugmeister', 'wissen:widget_search'),
-- Vikunja
('dashboard_zeugmeister', 'vikunja:create_tasks'),
('dashboard_zeugmeister', 'vikunja:widget_tasks'),
('dashboard_zeugmeister', 'vikunja:widget_quick_add'),
-- Dashboard
('dashboard_zeugmeister', 'dashboard:widget_links'),
('dashboard_zeugmeister', 'dashboard:widget_banner')
ON CONFLICT DO NOTHING;
-- ── dashboard_chargen — mid level ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender
('dashboard_chargen', 'kalender:view'),
('dashboard_chargen', 'kalender:create_bookings'),
('dashboard_chargen', 'kalender:cancel_own_bookings'),
('dashboard_chargen', 'kalender:widget_events'),
('dashboard_chargen', 'kalender:widget_bookings'),
-- Fahrzeuge
('dashboard_chargen', 'fahrzeuge:view'),
('dashboard_chargen', 'fahrzeuge:change_status'),
('dashboard_chargen', 'fahrzeuge:manage_maintenance'),
('dashboard_chargen', 'fahrzeuge:widget'),
-- Einsätze
('dashboard_chargen', 'einsaetze:view'),
('dashboard_chargen', 'einsaetze:create'),
('dashboard_chargen', 'einsaetze:manage_personnel'),
-- Ausrüstung
('dashboard_chargen', 'ausruestung:view'),
('dashboard_chargen', 'ausruestung:create'),
('dashboard_chargen', 'ausruestung:manage_maintenance'),
('dashboard_chargen', 'ausruestung:widget'),
-- Mitglieder
('dashboard_chargen', 'mitglieder:view_own'),
('dashboard_chargen', 'mitglieder:view_all'),
-- Atemschutz
('dashboard_chargen', 'atemschutz:view'),
('dashboard_chargen', 'atemschutz:create'),
('dashboard_chargen', 'atemschutz:widget'),
-- Wissen
('dashboard_chargen', 'wissen:view'),
('dashboard_chargen', 'wissen:widget_recent'),
('dashboard_chargen', 'wissen:widget_search'),
-- Vikunja
('dashboard_chargen', 'vikunja:create_tasks'),
('dashboard_chargen', 'vikunja:widget_tasks'),
('dashboard_chargen', 'vikunja:widget_quick_add'),
-- Dashboard
('dashboard_chargen', 'dashboard:widget_links'),
('dashboard_chargen', 'dashboard:widget_banner')
ON CONFLICT DO NOTHING;
-- ── dashboard_moderator — event/calendar management ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender
('dashboard_moderator', 'kalender:view'),
('dashboard_moderator', 'kalender:create'),
('dashboard_moderator', 'kalender:cancel_own_bookings'),
('dashboard_moderator', 'kalender:create_bookings'),
('dashboard_moderator', 'kalender:edit_bookings'),
('dashboard_moderator', 'kalender:manage_categories'),
('dashboard_moderator', 'kalender:widget_events'),
('dashboard_moderator', 'kalender:widget_bookings'),
('dashboard_moderator', 'kalender:widget_quick_add'),
-- Fahrzeuge
('dashboard_moderator', 'fahrzeuge:view'),
('dashboard_moderator', 'fahrzeuge:widget'),
-- Einsätze
('dashboard_moderator', 'einsaetze:view'),
-- Ausrüstung
('dashboard_moderator', 'ausruestung:view'),
('dashboard_moderator', 'ausruestung:widget'),
-- Mitglieder
('dashboard_moderator', 'mitglieder:view_own'),
('dashboard_moderator', 'mitglieder:view_all'),
-- Atemschutz
('dashboard_moderator', 'atemschutz:view'),
('dashboard_moderator', 'atemschutz:widget'),
-- Wissen
('dashboard_moderator', 'wissen:view'),
('dashboard_moderator', 'wissen:widget_recent'),
('dashboard_moderator', 'wissen:widget_search'),
-- Vikunja
('dashboard_moderator', 'vikunja:create_tasks'),
('dashboard_moderator', 'vikunja:widget_tasks'),
('dashboard_moderator', 'vikunja:widget_quick_add'),
-- Dashboard
('dashboard_moderator', 'dashboard:widget_links'),
('dashboard_moderator', 'dashboard:widget_banner')
ON CONFLICT DO NOTHING;
-- ── dashboard_atemschutz — atemschutz specialist ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender
('dashboard_atemschutz', 'kalender:view'),
('dashboard_atemschutz', 'kalender:create_bookings'),
('dashboard_atemschutz', 'kalender:cancel_own_bookings'),
('dashboard_atemschutz', 'kalender:widget_events'),
('dashboard_atemschutz', 'kalender:widget_bookings'),
-- Fahrzeuge
('dashboard_atemschutz', 'fahrzeuge:view'),
('dashboard_atemschutz', 'fahrzeuge:widget'),
-- Einsätze
('dashboard_atemschutz', 'einsaetze:view'),
-- Ausrüstung
('dashboard_atemschutz', 'ausruestung:view'),
('dashboard_atemschutz', 'ausruestung:widget'),
-- Mitglieder
('dashboard_atemschutz', 'mitglieder:view_own'),
('dashboard_atemschutz', 'mitglieder:view_all'),
-- Atemschutz
('dashboard_atemschutz', 'atemschutz:view'),
('dashboard_atemschutz', 'atemschutz:create'),
('dashboard_atemschutz', 'atemschutz:widget'),
-- Wissen
('dashboard_atemschutz', 'wissen:view'),
('dashboard_atemschutz', 'wissen:widget_recent'),
('dashboard_atemschutz', 'wissen:widget_search'),
-- Vikunja
('dashboard_atemschutz', 'vikunja:create_tasks'),
('dashboard_atemschutz', 'vikunja:widget_tasks'),
('dashboard_atemschutz', 'vikunja:widget_quick_add'),
-- Dashboard
('dashboard_atemschutz', 'dashboard:widget_links'),
('dashboard_atemschutz', 'dashboard:widget_banner')
ON CONFLICT DO NOTHING;
-- ── dashboard_mitglied — basic member ──
INSERT INTO group_permissions (authentik_group, permission_id) VALUES
-- Kalender
('dashboard_mitglied', 'kalender:view'),
('dashboard_mitglied', 'kalender:create_bookings'),
('dashboard_mitglied', 'kalender:cancel_own_bookings'),
('dashboard_mitglied', 'kalender:widget_events'),
('dashboard_mitglied', 'kalender:widget_bookings'),
-- Fahrzeuge
('dashboard_mitglied', 'fahrzeuge:view'),
('dashboard_mitglied', 'fahrzeuge:widget'),
-- Einsätze
('dashboard_mitglied', 'einsaetze:view'),
-- Ausrüstung
('dashboard_mitglied', 'ausruestung:view'),
('dashboard_mitglied', 'ausruestung:widget'),
-- Mitglieder
('dashboard_mitglied', 'mitglieder:view_own'),
-- Atemschutz
('dashboard_mitglied', 'atemschutz:widget'),
-- Wissen
('dashboard_mitglied', 'wissen:view'),
('dashboard_mitglied', 'wissen:widget_recent'),
('dashboard_mitglied', 'wissen:widget_search'),
-- Vikunja
('dashboard_mitglied', 'vikunja:create_tasks'),
('dashboard_mitglied', 'vikunja:widget_tasks'),
('dashboard_mitglied', 'vikunja:widget_quick_add'),
-- Dashboard
('dashboard_mitglied', 'dashboard:widget_links'),
('dashboard_mitglied', 'dashboard:widget_banner')
ON CONFLICT DO NOTHING;