2026-01-20 17:38:01 +05:30
|
|
|
import requests
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
from fastapi import HTTPException
|
|
|
|
|
from typing import List, Dict, Any
|
2026-02-02 17:33:35 +05:30
|
|
|
import hmac
|
|
|
|
|
import hashlib
|
2026-01-20 17:38:01 +05:30
|
|
|
from app.models.auth.module_model import Module
|
|
|
|
|
from app.models.auth.module_environment_model import ModuleEnvironment
|
|
|
|
|
from app.models.auth.access_model import Access
|
|
|
|
|
from app.services.auth.trust_service import TrustService
|
2026-02-02 17:33:35 +05:30
|
|
|
from app.models.auth.module_access_model import ModuleAccess
|
|
|
|
|
import uuid
|
|
|
|
|
from app.core.redis import sync_redis_client
|
2026-01-20 17:38:01 +05:30
|
|
|
|
|
|
|
|
class ModulePermissionService:
|
2026-02-02 17:33:35 +05:30
|
|
|
|
2026-01-20 17:38:01 +05:30
|
|
|
@staticmethod
|
|
|
|
|
def sync_permissions(db: Session, module_id: str):
|
|
|
|
|
"""
|
|
|
|
|
Connects to the module's default environment and fetches defined permissions.
|
2026-02-02 17:33:35 +05:30
|
|
|
Updates the local ModuleAccess table to mirror these permissions.
|
|
|
|
|
|
|
|
|
|
:param module_id: The UUID string of the module
|
2026-01-20 17:38:01 +05:30
|
|
|
"""
|
2026-02-02 17:33:35 +05:30
|
|
|
try:
|
|
|
|
|
module_uuid = uuid.UUID(module_id)
|
|
|
|
|
except ValueError:
|
|
|
|
|
raise HTTPException(status_code=400, detail="Invalid module UUID")
|
|
|
|
|
|
|
|
|
|
module = db.query(Module).filter(Module.id == module_uuid).first()
|
2026-01-20 17:38:01 +05:30
|
|
|
if not module:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Module not found")
|
|
|
|
|
|
|
|
|
|
env = db.query(ModuleEnvironment).filter(
|
|
|
|
|
ModuleEnvironment.module_id == module.id,
|
|
|
|
|
ModuleEnvironment.is_default == True
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
if not env:
|
|
|
|
|
env = db.query(ModuleEnvironment).filter(
|
|
|
|
|
ModuleEnvironment.module_id == module.id,
|
|
|
|
|
ModuleEnvironment.is_active == True
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
if not env:
|
|
|
|
|
raise HTTPException(status_code=400, detail="No active environment to sync from")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
payload_body = "{}"
|
2026-01-20 17:38:01 +05:30
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
secret = env.trust_credentials.get("hmac_secret")
|
|
|
|
|
if not secret:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
if secret:
|
|
|
|
|
signature = hmac.new(
|
|
|
|
|
secret.encode("utf-8"),
|
|
|
|
|
payload_body.encode("utf-8"),
|
|
|
|
|
hashlib.sha256
|
|
|
|
|
).hexdigest()
|
|
|
|
|
else:
|
|
|
|
|
signature = ""
|
|
|
|
|
|
2026-01-20 17:38:01 +05:30
|
|
|
headers = {
|
2026-02-02 17:33:35 +05:30
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"X-SaaS-Signature": signature
|
2026-01-20 17:38:01 +05:30
|
|
|
}
|
|
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
url = f"{env.backend_base_url}{env.permission_sync_endpoint}"
|
|
|
|
|
response = requests.post(url, headers=headers, data=payload_body, timeout=10)
|
2026-01-20 17:38:01 +05:30
|
|
|
response.raise_for_status()
|
2026-02-02 17:33:35 +05:30
|
|
|
data = response.json()
|
2026-01-20 17:38:01 +05:30
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise HTTPException(status_code=502, detail=f"Failed to fetch permissions from module: {str(e)}")
|
|
|
|
|
|
|
|
|
|
permissions: List[Dict[str, Any]] = data.get("permissions", [])
|
|
|
|
|
|
|
|
|
|
synced_count = 0
|
|
|
|
|
timestamp = datetime.now(timezone.utc)
|
|
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
permission_map = {}
|
|
|
|
|
|
2026-01-20 17:38:01 +05:30
|
|
|
for perm in permissions:
|
|
|
|
|
code = perm.get("permission_code")
|
|
|
|
|
if not code:
|
|
|
|
|
continue
|
|
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
access = db.query(ModuleAccess).filter(
|
|
|
|
|
ModuleAccess.module_id == module.id,
|
|
|
|
|
ModuleAccess.access_code == code
|
2026-01-20 17:38:01 +05:30
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
if not access:
|
2026-02-02 17:33:35 +05:30
|
|
|
access = ModuleAccess(
|
2026-01-20 17:38:01 +05:30
|
|
|
access_code=code,
|
|
|
|
|
module_id=module.id,
|
2026-02-02 17:33:35 +05:30
|
|
|
name=perm.get("name", code),
|
2026-01-20 17:38:01 +05:30
|
|
|
category=perm.get("category", "General"),
|
|
|
|
|
)
|
|
|
|
|
db.add(access)
|
|
|
|
|
else:
|
|
|
|
|
access.name = perm.get("name", access.name)
|
|
|
|
|
access.category = perm.get("category", access.category)
|
|
|
|
|
|
|
|
|
|
access.last_synced_at = timestamp
|
2026-02-02 17:33:35 +05:30
|
|
|
access.sync_checksum = perm.get("hash")
|
|
|
|
|
|
|
|
|
|
permission_map[code] = access
|
2026-01-20 17:38:01 +05:30
|
|
|
synced_count += 1
|
|
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
db.flush()
|
|
|
|
|
|
|
|
|
|
for perm in permissions:
|
|
|
|
|
code = perm.get("permission_code")
|
|
|
|
|
parent_code = perm.get("parent_code")
|
|
|
|
|
|
|
|
|
|
if not code or not parent_code:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
access = permission_map.get(code)
|
|
|
|
|
parent_access = permission_map.get(parent_code)
|
|
|
|
|
|
|
|
|
|
if not parent_access:
|
|
|
|
|
parent_access = db.query(ModuleAccess).filter(
|
|
|
|
|
ModuleAccess.module_id == module.id,
|
|
|
|
|
ModuleAccess.access_code == parent_code
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
if access and parent_access:
|
|
|
|
|
access.parent_id = parent_access.id
|
|
|
|
|
|
2026-01-20 17:38:01 +05:30
|
|
|
db.commit()
|
2026-02-02 17:33:35 +05:30
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if sync_redis_client.client:
|
|
|
|
|
sync_redis_client.client.delete("saas:access:v2:all:full")
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"status": "success",
|
|
|
|
|
"message": "Permissions synced successfully",
|
|
|
|
|
"synced_count": synced_count
|
|
|
|
|
}
|
2026-01-20 17:38:01 +05:30
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_module_permissions(db: Session, module_id: str):
|
|
|
|
|
"""List permissions for a module from local DB."""
|
2026-04-25 08:44:33 +03:00
|
|
|
try:
|
|
|
|
|
module_uuid = uuid.UUID(module_id)
|
|
|
|
|
module = db.query(Module).filter(Module.id == module_uuid).first()
|
|
|
|
|
except ValueError:
|
|
|
|
|
module = db.query(Module).filter(Module.module_id == module_id).first()
|
2026-01-20 17:38:01 +05:30
|
|
|
if not module:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Module not found")
|
|
|
|
|
|
2026-02-02 17:33:35 +05:30
|
|
|
return db.query(ModuleAccess).filter(
|
|
|
|
|
ModuleAccess.module_id == module.id
|
|
|
|
|
).all()
|