feat(rbac): add roles and permissions

This commit is contained in:
Schubert Ferenc 2026-07-02 23:05:59 +02:00
parent 86a32a942c
commit 694b7bd09a
37 changed files with 2682 additions and 218 deletions

View file

@ -0,0 +1,269 @@
"""add rbac
Revision ID: 7b2c9f4a0d31
Revises: 3f4c7b8d1e2a
Create Date: 2026-07-02 13:00:00.000000
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "7b2c9f4a0d31"
down_revision: Union[str, Sequence[str], None] = "3f4c7b8d1e2a"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
ROLES = [
("administrator", "Administrator", "Vollzugriff auf Olympus CRM"),
("management", "Management", "Lesender Zugriff auf operative Kernmodule"),
("sales", "Vertrieb", "Zugriff auf Kunden und Projekte"),
("technician", "Technik", "Zugriff auf Tickets und Projekte"),
("support", "Support", "Operativer Support-Zugriff"),
("warehouse", "Lager", "Zugriff auf lagernahe Prozesse"),
("guest", "Gast", "Minimaler Zugriff"),
]
PERMISSIONS = [
("users.read", "Benutzer lesen", "Benutzer anzeigen", "users"),
("users.create", "Benutzer erstellen", "Benutzer anlegen", "users"),
("users.update", "Benutzer bearbeiten", "Benutzer aktualisieren", "users"),
("users.delete", "Benutzer loeschen", "Benutzer entfernen", "users"),
("users.password.update", "Passwort aendern", "Benutzerpasswort aendern", "users"),
("roles.read", "Rollen lesen", "Rollen anzeigen", "roles"),
("roles.create", "Rollen erstellen", "Rollen anlegen", "roles"),
("roles.update", "Rollen bearbeiten", "Rollen aktualisieren", "roles"),
("roles.delete", "Rollen loeschen", "Rollen entfernen", "roles"),
("roles.assign", "Rollen zuweisen", "Berechtigungen Rollen zuweisen", "roles"),
("customers.read", "Kunden lesen", "Kunden anzeigen", "customers"),
("customers.create", "Kunden erstellen", "Kunden anlegen", "customers"),
("customers.update", "Kunden bearbeiten", "Kunden aktualisieren", "customers"),
("customers.delete", "Kunden loeschen", "Kunden entfernen", "customers"),
("projects.read", "Projekte lesen", "Projekte anzeigen", "projects"),
("projects.create", "Projekte erstellen", "Projekte anlegen", "projects"),
("projects.update", "Projekte bearbeiten", "Projekte aktualisieren", "projects"),
("projects.delete", "Projekte loeschen", "Projekte entfernen", "projects"),
("tickets.read", "Tickets lesen", "Tickets anzeigen", "tickets"),
("tickets.create", "Tickets erstellen", "Tickets anlegen", "tickets"),
("tickets.update", "Tickets bearbeiten", "Tickets aktualisieren", "tickets"),
("tickets.delete", "Tickets loeschen", "Tickets entfernen", "tickets"),
("dashboard.read", "Dashboard lesen", "Dashboard anzeigen", "dashboard"),
("system.settings.read", "Einstellungen lesen", "Systemeinstellungen anzeigen", "system"),
("system.settings.update", "Einstellungen bearbeiten", "Systemeinstellungen aktualisieren", "system"),
]
def upgrade() -> None:
op.create_table(
"roles",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=80), nullable=False),
sa.Column("display_name", sa.String(length=120), nullable=False),
sa.Column("description", sa.String(length=500), server_default="", nullable=False),
sa.Column("is_system", sa.Boolean(), server_default=sa.true(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_roles_name"), "roles", ["name"], unique=True)
op.create_table(
"permissions",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=120), nullable=False),
sa.Column("display_name", sa.String(length=160), nullable=False),
sa.Column("description", sa.String(length=500), server_default="", nullable=False),
sa.Column("module", sa.String(length=80), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_permissions_module"), "permissions", ["module"], unique=False)
op.create_index(op.f("ix_permissions_name"), "permissions", ["name"], unique=True)
op.create_table(
"role_permissions",
sa.Column("role_id", sa.Integer(), nullable=False),
sa.Column("permission_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["permission_id"], ["permissions.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("role_id", "permission_id"),
)
roles_table = sa.table(
"roles",
sa.column("name", sa.String),
sa.column("display_name", sa.String),
sa.column("description", sa.String),
sa.column("is_system", sa.Boolean),
)
permissions_table = sa.table(
"permissions",
sa.column("name", sa.String),
sa.column("display_name", sa.String),
sa.column("description", sa.String),
sa.column("module", sa.String),
)
op.bulk_insert(
roles_table,
[
{
"name": name,
"display_name": display_name,
"description": description,
"is_system": True,
}
for name, display_name, description in ROLES
],
)
op.bulk_insert(
permissions_table,
[
{
"name": name,
"display_name": display_name,
"description": description,
"module": module,
}
for name, display_name, description, module in PERMISSIONS
],
)
op.execute(
"""
INSERT INTO role_permissions (role_id, permission_id)
SELECT roles.id, permissions.id
FROM roles
CROSS JOIN permissions
WHERE roles.name = 'administrator'
"""
)
op.execute(
"""
INSERT INTO role_permissions (role_id, permission_id)
SELECT roles.id, permissions.id
FROM roles
JOIN permissions ON permissions.name IN (
'dashboard.read',
'users.read',
'customers.read',
'projects.read',
'tickets.read'
)
WHERE roles.name = 'management'
"""
)
op.execute(
"""
INSERT INTO role_permissions (role_id, permission_id)
SELECT roles.id, permissions.id
FROM roles
JOIN permissions ON permissions.name IN (
'dashboard.read',
'customers.read',
'customers.create',
'customers.update',
'projects.read',
'tickets.read'
)
WHERE roles.name = 'sales'
"""
)
op.execute(
"""
INSERT INTO role_permissions (role_id, permission_id)
SELECT roles.id, permissions.id
FROM roles
JOIN permissions ON permissions.name IN (
'dashboard.read',
'projects.read',
'tickets.read',
'tickets.create',
'tickets.update'
)
WHERE roles.name = 'technician'
"""
)
op.execute(
"""
INSERT INTO role_permissions (role_id, permission_id)
SELECT roles.id, permissions.id
FROM roles
JOIN permissions ON permissions.name IN (
'dashboard.read',
'users.read',
'customers.read',
'tickets.read',
'tickets.create',
'tickets.update'
)
WHERE roles.name = 'support'
"""
)
op.execute(
"""
INSERT INTO role_permissions (role_id, permission_id)
SELECT roles.id, permissions.id
FROM roles
JOIN permissions ON permissions.name IN (
'dashboard.read'
)
WHERE roles.name IN ('warehouse', 'guest')
"""
)
op.add_column("users", sa.Column("role_id", sa.Integer(), nullable=True))
op.create_index(op.f("ix_users_role_id"), "users", ["role_id"], unique=False)
op.create_foreign_key("fk_users_role_id_roles", "users", "roles", ["role_id"], ["id"])
op.execute(
"""
UPDATE users
SET role_id = roles.id
FROM roles
WHERE roles.name = CASE
WHEN users.role IN ('admin', 'administrator') THEN 'administrator'
WHEN users.role IN ('manager', 'management') THEN 'management'
WHEN users.role = 'sales' THEN 'sales'
WHEN users.role IN ('technician', 'tech') THEN 'technician'
WHEN users.role = 'warehouse' THEN 'warehouse'
WHEN users.role = 'guest' THEN 'guest'
ELSE 'support'
END
"""
)
op.execute(
"""
UPDATE users
SET role_id = (SELECT id FROM roles WHERE name = 'administrator')
WHERE id = (
SELECT id
FROM users
ORDER BY id
LIMIT 1
)
AND NOT EXISTS (
SELECT 1
FROM users
JOIN roles ON users.role_id = roles.id
WHERE roles.name = 'administrator'
)
"""
)
op.alter_column("users", "role_id", existing_type=sa.Integer(), nullable=False)
def downgrade() -> None:
op.drop_constraint("fk_users_role_id_roles", "users", type_="foreignkey")
op.drop_index(op.f("ix_users_role_id"), table_name="users")
op.drop_column("users", "role_id")
op.drop_table("role_permissions")
op.drop_index(op.f("ix_permissions_name"), table_name="permissions")
op.drop_index(op.f("ix_permissions_module"), table_name="permissions")
op.drop_table("permissions")
op.drop_index(op.f("ix_roles_name"), table_name="roles")
op.drop_table("roles")