jogosultsági szintek RBAC beállítva tesztelve
This commit is contained in:
135
backend/app/models/system/rbac.py
Normal file
135
backend/app/models/system/rbac.py
Normal file
@@ -0,0 +1,135 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/system/rbac.py
|
||||
"""
|
||||
RBAC Phase 1: Database-Driven Role-Based Access Control models.
|
||||
|
||||
These models replace the hardcoded SYSTEM_CAPABILITIES_MATRIX and UserRole enum
|
||||
with fully database-driven roles, permissions, and role-permission mappings.
|
||||
|
||||
Schema: system
|
||||
Tables:
|
||||
- system.roles
|
||||
- system.permissions
|
||||
- system.role_permissions
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
from sqlalchemy import String, Integer, Boolean, DateTime, ForeignKey, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from app.database import Base
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class SystemRole(Base):
|
||||
"""
|
||||
System-level roles for the entire platform.
|
||||
|
||||
Replaces the hardcoded UserRole enum. Each role has a hierarchical rank
|
||||
(100=SUPERADMIN, 0=USER) for quick privilege comparisons.
|
||||
System roles are protected (is_system=True) and cannot be deleted via admin UI.
|
||||
"""
|
||||
__tablename__ = "roles"
|
||||
__table_args__ = (
|
||||
{"schema": "system", "extend_existing": True}
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, index=True)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
rank: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
is_system: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
updated_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
DateTime(timezone=True), onupdate=func.now(), nullable=True
|
||||
)
|
||||
|
||||
# --- RELATIONSHIPS ---
|
||||
permissions: Mapped[List["SystemRolePermission"]] = relationship(
|
||||
"SystemRolePermission", back_populates="role", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<SystemRole(id={self.id}, name='{self.name}', rank={self.rank})>"
|
||||
|
||||
|
||||
class SystemPermission(Base):
|
||||
"""
|
||||
Granular permission codes for the entire platform.
|
||||
|
||||
Format: domain:action (e.g., fleet:view, user:create, finance:approve)
|
||||
|
||||
Domains: fleet, user, finance, system, org, reports, audit, settings
|
||||
Actions: view, create, edit, delete, approve, manage, export, refund, manage-roles
|
||||
"""
|
||||
__tablename__ = "permissions"
|
||||
__table_args__ = (
|
||||
{"schema": "system", "extend_existing": True}
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
|
||||
domain: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
||||
action: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
is_system: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
|
||||
# --- RELATIONSHIPS ---
|
||||
role_assignments: Mapped[List["SystemRolePermission"]] = relationship(
|
||||
"SystemRolePermission", back_populates="permission", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<SystemPermission(id={self.id}, code='{self.code}')>"
|
||||
|
||||
|
||||
class SystemRolePermission(Base):
|
||||
"""
|
||||
Many-to-many mapping between roles and permissions with explicit grant/deny.
|
||||
|
||||
The `granted` boolean allows explicit denial of a permission for a role,
|
||||
which can override inherited permissions in hierarchical role structures.
|
||||
"""
|
||||
__tablename__ = "role_permissions"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("role_id", "permission_id", name="uix_role_permission"),
|
||||
{"schema": "system", "extend_existing": True}
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
role_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("system.roles.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
permission_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("system.permissions.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
granted: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
|
||||
# --- RELATIONSHIPS ---
|
||||
role: Mapped["SystemRole"] = relationship("SystemRole", back_populates="permissions")
|
||||
permission: Mapped["SystemPermission"] = relationship(
|
||||
"SystemPermission", back_populates="role_assignments"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<SystemRolePermission(id={self.id}, "
|
||||
f"role_id={self.role_id}, "
|
||||
f"permission_id={self.permission_id}, "
|
||||
f"granted={self.granted})>"
|
||||
)
|
||||
Reference in New Issue
Block a user