feat: Implement multi-tenancy support in middleware and security layers
- Enhanced TenantMiddleware to validate tenant information from JWT tokens. - Added LicenseValidationMiddleware to check tenant licenses before processing requests. - Updated security utilities to extract tenant information from tokens and validate company access. - Introduced CompanyStore to manage active company state and handle company switching in the frontend. - Modified API routes to include company_id in requests for better resource management. - Improved logging and error handling throughout the middleware and API layers. - Updated frontend components to reflect changes in company management and selection. - Added new API route for fetching user's companies with proper authentication handling.
This commit is contained in:
@@ -1,58 +1,63 @@
|
||||
"""
|
||||
DTOs para módulo de autenticación
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class LoginRequestDTO(BaseModel):
|
||||
"""DTO para solicitud de login"""
|
||||
|
||||
username: str = Field(..., description="Usuario o email")
|
||||
password: str = Field(..., min_length=6, description="Contraseña")
|
||||
tenant_slug: str = Field(..., description="Slug del tenant")
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"username": "usuario@ejemplo.com",
|
||||
"password": "password123",
|
||||
"tenant_slug": "empresa-abc"
|
||||
"tenant_slug": "empresa-abc",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TokenResponseDTO(BaseModel):
|
||||
"""DTO para respuesta de token"""
|
||||
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
expires_in: int
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3600
|
||||
"expires_in": 3600,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RefreshTokenRequestDTO(BaseModel):
|
||||
"""DTO para solicitud de refresh token"""
|
||||
|
||||
refresh_token: str = Field(..., description="Refresh token")
|
||||
|
||||
|
||||
class UserInfoResponseDTO(BaseModel):
|
||||
"""DTO para información de usuario"""
|
||||
|
||||
sub: str
|
||||
email: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
preferred_username: Optional[str] = None
|
||||
tenant_id: Optional[int] = None
|
||||
roles: list[str] = []
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
@@ -61,25 +66,29 @@ class UserInfoResponseDTO(BaseModel):
|
||||
"name": "Juan Pérez",
|
||||
"preferred_username": "jperez",
|
||||
"tenant_id": 1,
|
||||
"roles": ["user", "admin"]
|
||||
"roles": ["user", "admin"],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LogoutRequestDTO(BaseModel):
|
||||
"""DTO para solicitud de logout"""
|
||||
|
||||
refresh_token: str = Field(..., description="Refresh token para invalidar")
|
||||
|
||||
|
||||
class RegisterRequestDTO(BaseModel):
|
||||
"""DTO para solicitud de registro"""
|
||||
username: str = Field(..., min_length=3, max_length=50, description="Nombre de usuario")
|
||||
|
||||
username: str = Field(
|
||||
..., min_length=3, max_length=50, description="Nombre de usuario"
|
||||
)
|
||||
email: EmailStr = Field(..., description="Email del usuario")
|
||||
password: str = Field(..., min_length=8, description="Contraseña")
|
||||
first_name: str = Field(..., min_length=2, max_length=50, description="Nombre")
|
||||
last_name: str = Field(..., min_length=2, max_length=50, description="Apellido")
|
||||
tenant_slug: str = Field(..., description="Slug del tenant")
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
@@ -88,54 +97,57 @@ class RegisterRequestDTO(BaseModel):
|
||||
"password": "MiPassword123!",
|
||||
"first_name": "Juan",
|
||||
"last_name": "Pérez",
|
||||
"tenant_slug": "empresa-abc"
|
||||
"tenant_slug": "empresa-abc",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RegisterResponseDTO(BaseModel):
|
||||
"""DTO para respuesta de registro"""
|
||||
|
||||
user_id: str
|
||||
username: str
|
||||
email: str
|
||||
message: str
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"username": "jperez",
|
||||
"email": "jperez@ejemplo.com",
|
||||
"message": "User registered successfully"
|
||||
"message": "User registered successfully",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ExchangeCodeRequestDTO(BaseModel):
|
||||
"""DTO para intercambiar authorization code por tokens (OAuth2 flow)"""
|
||||
|
||||
code: str = Field(..., description="Authorization code de OAuth2")
|
||||
redirect_uri: str = Field(..., description="Redirect URI usado en la autorización")
|
||||
tenant_slug: Optional[str] = Field(None, description="Slug del tenant (opcional)")
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"code": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Ii...",
|
||||
"redirect_uri": "http://localhost:5173/auth/callback",
|
||||
"tenant_slug": "empresa-abc"
|
||||
"tenant_slug": "empresa-abc",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SetCookieRequestDTO(BaseModel):
|
||||
"""DTO para establecer cookies de autenticación"""
|
||||
|
||||
access_token: str = Field(..., description="Access token JWT")
|
||||
refresh_token: str = Field(..., description="Refresh token JWT")
|
||||
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user