jogosultsági szintek RBAC beállítva tesztelve

This commit is contained in:
Roo
2026-06-25 20:41:49 +00:00
parent 52011606ff
commit 36109fc722
242 changed files with 8672 additions and 2237 deletions

View File

@@ -44,6 +44,7 @@ from .identity.social import ServiceProvider, Vote, Competition, UserScore, Serv
from .gamification.gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, PointsLedger, UserContribution, Season
from .system.system import SystemParameter, ParameterScope, InternalNotification, SystemServiceStaging
from .system.rbac import SystemRole, SystemPermission, SystemRolePermission
from .system.document import Document
from .system.translation import Translation
@@ -96,4 +97,6 @@ __all__ = [
"Campaign", "Creative", "Placement", "CampaignCreative",
"CampaignPlacement", "AdImpression", "AdClick",
"CampaignStatus", "CreativeType", "PlacementType",
# RBAC Phase 1
"SystemRole", "SystemPermission", "SystemRolePermission",
]

View File

@@ -20,6 +20,7 @@ if TYPE_CHECKING:
from .payment import PaymentIntent, WithdrawalRequest
from .social import ServiceReview, SocialAccount
from ..marketplace.service_request import ServiceRequest
from ..system.rbac import SystemRole
class UserRole(str, enum.Enum):
"""Rendszerszintű (System-level) szerepkörök.
@@ -132,6 +133,15 @@ class User(Base):
email: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
hashed_password: Mapped[Optional[str]] = mapped_column(String)
# RBAC Phase 1: DB-driven role FK (nullable initially for migration safety)
role_id: Mapped[Optional[int]] = mapped_column(
Integer,
ForeignKey("system.roles.id", ondelete="SET NULL"),
nullable=True,
index=True
)
# Legacy: Keep UserRole enum column for backward compatibility during migration
role: Mapped[UserRole] = mapped_column(
PG_ENUM(UserRole, name="userrole", schema="identity"),
default=UserRole.USER
@@ -188,10 +198,17 @@ class User(Base):
# --- KAPCSOLATOK ---
# RBAC Phase 1: DB-driven role relationship
system_role: Mapped[Optional["SystemRole"]] = relationship(
"SystemRole",
foreign_keys=[role_id],
lazy="joined"
)
# JAVÍTÁS 4: Itt is explicit megadjuk, hogy melyik kulcs köti az emberhez
person: Mapped[Optional["Person"]] = relationship(
"Person",
foreign_keys=[person_id],
"Person",
foreign_keys=[person_id],
back_populates="users"
)

View File

@@ -29,12 +29,15 @@ class OrgUserRole(str, enum.Enum):
Ezek a szerepkörök a szervezeten/flottán belüli jogosultságokat határozzák meg.
Minden szerepkörhöz egyedi JSON permissions objektum tartozik a fleet.org_roles táblában.
Megjegyzés: Az értékek szinkronban kell lenniük a PostgreSQL fleet.orguserrole enum típussal.
Jelenlegi DB enum: OWNER, ADMIN, MANAGER, MEMBER, AGENT
"""
OWNER = "OWNER"
ADMIN = "ADMIN"
ACCOUNTANT = "ACCOUNTANT"
DRIVER = "DRIVER"
VIEWER = "VIEWER"
MANAGER = "MANAGER"
MEMBER = "MEMBER"
AGENT = "AGENT"
class OrgRole(Base):
"""

View File

@@ -4,9 +4,11 @@ from .audit import SecurityAuditLog, OperationalLog, ProcessLog, FinancialLedger
from .document import Document
from .translation import Translation
from .legal import LegalDocument, LegalAcceptance
from .rbac import SystemRole, SystemPermission, SystemRolePermission
__all__ = [
"SystemParameter", "InternalNotification", "SystemServiceStaging",
"SecurityAuditLog", "ProcessLog", "FinancialLedger", "WalletType", "LedgerStatus", "LedgerEntryType",
"Document", "Translation", "LegalDocument", "LegalAcceptance"
"Document", "Translation", "LegalDocument", "LegalAcceptance",
"SystemRole", "SystemPermission", "SystemRolePermission",
]

View 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})>"
)