Permisos en progreso
This commit is contained in:
@@ -28,6 +28,9 @@ async def create_tenant(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user = Depends(deps.get_current_active_superuser)
|
||||
):
|
||||
from app.models.permission import TenantPermission, DEFAULT_PERMISSIONS
|
||||
from datetime import datetime
|
||||
|
||||
# Check existing slug
|
||||
query = select(Tenant).where(Tenant.slug == tenant.slug)
|
||||
result = await db.execute(query)
|
||||
@@ -38,6 +41,23 @@ async def create_tenant(
|
||||
data['slug'] = data['slug'].lower().strip()
|
||||
db_tenant = Tenant(**data)
|
||||
db.add(db_tenant)
|
||||
await db.flush() # genera el id sin hacer commit
|
||||
|
||||
# Inicializar permisos por defecto para el nuevo tenant
|
||||
now = datetime.utcnow()
|
||||
for role, perms in DEFAULT_PERMISSIONS.items():
|
||||
for permission, granted in perms.items():
|
||||
db.add(TenantPermission(
|
||||
id=uuid.uuid4(),
|
||||
tenant_id=db_tenant.id,
|
||||
user_id=None,
|
||||
role=role,
|
||||
permission=permission,
|
||||
granted=granted,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
))
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(db_tenant)
|
||||
return db_tenant
|
||||
|
||||
@@ -11,6 +11,7 @@ from .client_profile import ClientProfile
|
||||
from .attachment import TicketAttachment
|
||||
from .audit import AuditLog
|
||||
from .refresh_token import RefreshToken
|
||||
from .permission import TenantPermission
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
@@ -18,6 +19,7 @@ __all__ = [
|
||||
"Ticket",
|
||||
"TicketIssue",
|
||||
"TicketComment",
|
||||
"TenantPermission",
|
||||
"System",
|
||||
"Category",
|
||||
"ClientProfile",
|
||||
|
||||
67
backend/app/models/permission.py
Normal file
67
backend/app/models/permission.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
TenantPermission Model - ServiceManagerWeb
|
||||
|
||||
Permisos dinámicos por tenant.
|
||||
- Si user_id es None → aplica al rol completo (default del tenant)
|
||||
- Si user_id tiene valor → override individual para ese usuario
|
||||
"""
|
||||
from sqlalchemy import String, Boolean, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from app.core.database import Base, GUID
|
||||
|
||||
|
||||
CLIENT_PERMISSIONS = [
|
||||
"view_tickets",
|
||||
"create_tickets",
|
||||
"close_tickets",
|
||||
"view_reports",
|
||||
"manage_tenant_users",
|
||||
"create_issues",
|
||||
]
|
||||
|
||||
DEFAULT_PERMISSIONS = {
|
||||
"CLIENT_ADMIN": {p: True for p in CLIENT_PERMISSIONS},
|
||||
"CLIENT_USER": {
|
||||
"view_tickets": True,
|
||||
"create_tickets": True,
|
||||
"close_tickets": False,
|
||||
"view_reports": False,
|
||||
"manage_tenant_users": False,
|
||||
"create_issues": False,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TenantPermission(Base):
|
||||
__tablename__ = "tenant_permissions"
|
||||
|
||||
tenant_id: Mapped[uuid.UUID] = mapped_column(
|
||||
GUID(),
|
||||
ForeignKey("tenants.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
user_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
GUID(),
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
role: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
permission: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
granted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
|
||||
tenant: Mapped["Tenant"] = relationship("Tenant")
|
||||
user: Mapped[Optional["User"]] = relationship("User")
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"tenant_id", "role", "user_id", "permission",
|
||||
name="uq_tenant_permission"
|
||||
),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
scope = f"user:{self.user_id}" if self.user_id else f"role:{self.role}"
|
||||
return f"<TenantPermission({scope} {self.permission}={'✓' if self.granted else '✗'})>"
|
||||
Reference in New Issue
Block a user