feat(auth): gate module SSO by user assignment
This commit is contained in:
@@ -18,7 +18,9 @@ class ModuleController:
|
||||
@staticmethod
|
||||
def get_available_modules(db: Session, current_user) -> List[ModuleResponse]:
|
||||
from app.schemas.auth.module_schema import ModuleAvailableResponse
|
||||
results = ModuleService.get_available_modules(db, current_user.tenant_id)
|
||||
results = ModuleService.get_available_modules(
|
||||
db, current_user.tenant_id, current_user.role_id
|
||||
)
|
||||
|
||||
response = []
|
||||
for item in results:
|
||||
@@ -46,4 +48,4 @@ class ModuleController:
|
||||
@staticmethod
|
||||
def delete_module(db: Session, module_id: str):
|
||||
ModuleService.delete_module(db, module_id)
|
||||
return {"message": "Module deleted successfully"}
|
||||
return {"message": "Module deleted successfully"}
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import List, Optional
|
||||
import uuid
|
||||
from app.models.auth.tenant_module_model import TenantModule
|
||||
from app.models.auth.module_model import Module
|
||||
from app.models.auth.module_access_model import ModuleAccess
|
||||
from app.models.auth.role_module_access_model import RoleModuleAccess
|
||||
from app.schemas.auth.module_schema import ModuleCreate, ModuleUpdate
|
||||
|
||||
class ModuleService:
|
||||
@@ -39,15 +41,25 @@ class ModuleService:
|
||||
return module
|
||||
|
||||
@staticmethod
|
||||
def get_available_modules(db: Session, tenant_id: Optional[uuid.UUID]) -> List[dict]:
|
||||
def get_available_modules(
|
||||
db: Session,
|
||||
tenant_id: Optional[uuid.UUID],
|
||||
role_id: Optional[uuid.UUID] = None,
|
||||
) -> List[dict]:
|
||||
if tenant_id:
|
||||
results = db.query(Module, TenantModule.is_active).join(
|
||||
TenantModule,
|
||||
(TenantModule.module_id == Module.id) & (TenantModule.tenant_id == tenant_id)
|
||||
).join(
|
||||
ModuleAccess, ModuleAccess.module_id == Module.id
|
||||
).join(
|
||||
RoleModuleAccess,
|
||||
(RoleModuleAccess.module_access_id == ModuleAccess.id) &
|
||||
(RoleModuleAccess.role_id == role_id)
|
||||
).filter(
|
||||
Module.status == "active",
|
||||
TenantModule.is_active == True
|
||||
).order_by(Module.display_order).all()
|
||||
).distinct().order_by(Module.display_order).all()
|
||||
|
||||
return [{"module": mod, "is_active": is_active} for mod, is_active in results]
|
||||
else:
|
||||
@@ -58,6 +70,17 @@ class ModuleService:
|
||||
def get_module_by_module_id(db: Session, module_id_str: str) -> Optional[Module]:
|
||||
return db.query(Module).filter(Module.module_id == module_id_str).first()
|
||||
|
||||
@staticmethod
|
||||
def role_can_launch_module(db: Session, role_id: Optional[uuid.UUID], module_id: uuid.UUID) -> bool:
|
||||
if not role_id:
|
||||
return False
|
||||
return db.query(RoleModuleAccess.id).join(
|
||||
ModuleAccess, ModuleAccess.id == RoleModuleAccess.module_access_id
|
||||
).filter(
|
||||
RoleModuleAccess.role_id == role_id,
|
||||
ModuleAccess.module_id == module_id,
|
||||
).first() is not None
|
||||
|
||||
@staticmethod
|
||||
def update_module(db: Session, module_id: str, module_data: ModuleUpdate) -> Module:
|
||||
module = ModuleService.get_module(db, module_id)
|
||||
@@ -74,4 +97,4 @@ class ModuleService:
|
||||
def delete_module(db: Session, module_id: str):
|
||||
module = ModuleService.get_module(db, module_id)
|
||||
db.delete(module)
|
||||
db.commit()
|
||||
db.commit()
|
||||
|
||||
@@ -18,6 +18,7 @@ import time
|
||||
from app.services.auth.subscription_entitlement_service import (
|
||||
SubscriptionEntitlementService,
|
||||
)
|
||||
from app.services.auth.module_service import ModuleService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -50,6 +51,10 @@ class SSOService:
|
||||
|
||||
if not tm or not tm.is_active:
|
||||
raise HTTPException(status_code=403, detail="Tenant does not have access to this module")
|
||||
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user or not ModuleService.role_can_launch_module(db, user.role_id, module.id):
|
||||
raise HTTPException(status_code=403, detail="User is not assigned to this module")
|
||||
|
||||
if tm.assigned_environment_slug:
|
||||
environment_slug = tm.assigned_environment_slug
|
||||
@@ -114,7 +119,12 @@ class SSOService:
|
||||
environment_slug = "prod"
|
||||
if tenant_id:
|
||||
tm = db.query(TenantModule).filter(TenantModule.tenant_id == tenant_id, TenantModule.module_id == module.id).first()
|
||||
if tm and tm.is_active and tm.assigned_environment_slug:
|
||||
if not tm or not tm.is_active:
|
||||
raise HTTPException(status_code=403, detail="Tenant does not have access to this module")
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user or not ModuleService.role_can_launch_module(db, user.role_id, module.id):
|
||||
raise HTTPException(status_code=403, detail="User is not assigned to this module")
|
||||
if tm.assigned_environment_slug:
|
||||
environment_slug = tm.assigned_environment_slug
|
||||
|
||||
env = db.query(ModuleEnvironment).filter(ModuleEnvironment.module_id == module.id, ModuleEnvironment.slug == environment_slug).first()
|
||||
@@ -128,11 +138,9 @@ class SSOService:
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
permissions = sorted(
|
||||
SubscriptionEntitlementService.get_effective_module_access_codes(
|
||||
db, user, module.id
|
||||
)
|
||||
)
|
||||
# SaaS controls identity and module entry. Operational authorization is
|
||||
# deliberately owned by the destination module.
|
||||
permissions = []
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
subscription_details = SubscriptionEntitlementService.get_subscription_summary(
|
||||
|
||||
Reference in New Issue
Block a user