feat(admin): implement group-centric relations and system/module/company linkage views
This commit is contained in:
@@ -13,6 +13,10 @@ from app.repositories.users_repo import UsersRepository
|
||||
from app.schemas.catalog import (
|
||||
CompanyCreateRequest,
|
||||
CompanyItem,
|
||||
GroupBindingSnapshot,
|
||||
GroupBindingUpdateRequest,
|
||||
GroupRelationItem,
|
||||
MemberRelationItem,
|
||||
CompanyUpdateRequest,
|
||||
MemberItem,
|
||||
MemberPermissionGroupsResponse,
|
||||
@@ -69,6 +73,14 @@ def _resolve_scope_ids(db: Session, scope_type: str, scope_id: str) -> tuple[str
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid_scope_type")
|
||||
|
||||
|
||||
def _split_module_key(payload_module: str | None) -> str:
|
||||
if not payload_module:
|
||||
return "__system__"
|
||||
if "." in payload_module:
|
||||
return payload_module.split(".", 1)[1]
|
||||
return payload_module
|
||||
|
||||
|
||||
def _sync_member_to_authentik(
|
||||
*,
|
||||
authentik_sub: str,
|
||||
@@ -195,6 +207,96 @@ def update_module(
|
||||
return ModuleItem(id=row.id, system_key=system_key, module_key=row.module_key, name=row.name, status=row.status)
|
||||
|
||||
|
||||
@router.get("/systems/{system_key}/groups")
|
||||
def list_system_groups(
|
||||
system_key: str,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, list[dict]]:
|
||||
systems_repo = SystemsRepository(db)
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
if not systems_repo.get_by_key(system_key):
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="system_not_found")
|
||||
groups = groups_repo.list_system_groups(system_key)
|
||||
return {
|
||||
"items": [
|
||||
GroupRelationItem(group_key=g.group_key, group_name=g.name, status=g.status).model_dump()
|
||||
for g in groups
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/systems/{system_key}/members")
|
||||
def list_system_members(
|
||||
system_key: str,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, list[dict]]:
|
||||
systems_repo = SystemsRepository(db)
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
if not systems_repo.get_by_key(system_key):
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="system_not_found")
|
||||
members = groups_repo.list_system_members(system_key)
|
||||
return {
|
||||
"items": [
|
||||
MemberRelationItem(
|
||||
authentik_sub=m.authentik_sub,
|
||||
email=m.email,
|
||||
display_name=m.display_name,
|
||||
is_active=m.is_active,
|
||||
).model_dump()
|
||||
for m in members
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/modules/{module_key}/groups")
|
||||
def list_module_groups(
|
||||
module_key: str,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, list[dict]]:
|
||||
modules_repo = ModulesRepository(db)
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
module = modules_repo.get_by_key(module_key)
|
||||
if not module:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="module_not_found")
|
||||
system_key, module_name = module.module_key.split(".", 1) if "." in module.module_key else ("", module.module_key)
|
||||
groups = groups_repo.list_module_groups(system_key, module_name)
|
||||
return {
|
||||
"items": [
|
||||
GroupRelationItem(group_key=g.group_key, group_name=g.name, status=g.status).model_dump()
|
||||
for g in groups
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/modules/{module_key}/members")
|
||||
def list_module_members(
|
||||
module_key: str,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, list[dict]]:
|
||||
modules_repo = ModulesRepository(db)
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
module = modules_repo.get_by_key(module_key)
|
||||
if not module:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="module_not_found")
|
||||
system_key, module_name = module.module_key.split(".", 1) if "." in module.module_key else ("", module.module_key)
|
||||
members = groups_repo.list_module_members(system_key, module_name)
|
||||
return {
|
||||
"items": [
|
||||
MemberRelationItem(
|
||||
authentik_sub=m.authentik_sub,
|
||||
email=m.email,
|
||||
display_name=m.display_name,
|
||||
is_active=m.is_active,
|
||||
).model_dump()
|
||||
for m in members
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/companies")
|
||||
def list_companies(
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
@@ -236,6 +338,32 @@ def update_company(
|
||||
return CompanyItem(id=row.id, company_key=row.company_key, name=row.name, status=row.status)
|
||||
|
||||
|
||||
@router.get("/companies/{company_key}/sites")
|
||||
def list_company_sites(
|
||||
company_key: str,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, list[dict]]:
|
||||
companies_repo = CompaniesRepository(db)
|
||||
sites_repo = SitesRepository(db)
|
||||
company = companies_repo.get_by_key(company_key)
|
||||
if not company:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="company_not_found")
|
||||
items, _ = sites_repo.list(company_id=company.id, limit=1000, offset=0)
|
||||
return {
|
||||
"items": [
|
||||
SiteItem(
|
||||
id=i.id,
|
||||
site_key=i.site_key,
|
||||
company_key=company.company_key,
|
||||
name=i.name,
|
||||
status=i.status,
|
||||
).model_dump()
|
||||
for i in items
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/sites")
|
||||
def list_sites(
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
@@ -481,16 +609,78 @@ def list_permission_group_permissions(
|
||||
PermissionGroupPermissionItem(
|
||||
id=r.id,
|
||||
system=r.system,
|
||||
module=r.module,
|
||||
module=("" if r.module == "__system__" else (r.module if "." in r.module else f"{r.system}.{r.module}")),
|
||||
action=r.action,
|
||||
scope_type=r.scope_type,
|
||||
scope_id=r.scope_id,
|
||||
).model_dump()
|
||||
for r in rows
|
||||
if r.action in {"view", "edit"} and r.scope_type == "site"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/permission-groups/{group_key}/bindings", response_model=GroupBindingSnapshot)
|
||||
def get_permission_group_bindings(
|
||||
group_key: str,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> GroupBindingSnapshot:
|
||||
repo = PermissionGroupsRepository(db)
|
||||
group = repo.get_by_key(group_key)
|
||||
if not group:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
|
||||
snapshot = repo.get_group_binding_snapshot(group.id, group_key)
|
||||
return GroupBindingSnapshot(**snapshot)
|
||||
|
||||
|
||||
@router.put("/permission-groups/{group_key}/bindings", response_model=GroupBindingSnapshot)
|
||||
def replace_permission_group_bindings(
|
||||
group_key: str,
|
||||
payload: GroupBindingUpdateRequest,
|
||||
_: ApiClient = Depends(require_api_client),
|
||||
db: Session = Depends(get_db),
|
||||
) -> GroupBindingSnapshot:
|
||||
repo = PermissionGroupsRepository(db)
|
||||
sites_repo = SitesRepository(db)
|
||||
systems_repo = SystemsRepository(db)
|
||||
modules_repo = ModulesRepository(db)
|
||||
|
||||
group = repo.get_by_key(group_key)
|
||||
if not group:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
|
||||
|
||||
site_keys = list(dict.fromkeys(payload.site_keys))
|
||||
system_keys = list(dict.fromkeys(payload.system_keys))
|
||||
module_keys = list(dict.fromkeys(payload.module_keys))
|
||||
|
||||
valid_sites = {s.site_key for s in sites_repo.list(limit=10000, offset=0)[0]}
|
||||
missing_sites = [k for k in site_keys if k not in valid_sites]
|
||||
if missing_sites:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"site_not_found:{','.join(missing_sites)}")
|
||||
|
||||
valid_systems = {s.system_key for s in systems_repo.list(limit=10000, offset=0)[0]}
|
||||
missing_systems = [k for k in system_keys if k not in valid_systems]
|
||||
if missing_systems:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"system_not_found:{','.join(missing_systems)}")
|
||||
|
||||
valid_modules = {m.module_key for m in modules_repo.list(limit=10000, offset=0)[0]}
|
||||
missing_modules = [k for k in module_keys if k not in valid_modules]
|
||||
if missing_modules:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"module_not_found:{','.join(missing_modules)}")
|
||||
|
||||
repo.replace_group_bindings(
|
||||
group_id=group.id,
|
||||
site_keys=site_keys,
|
||||
system_keys=system_keys,
|
||||
module_keys=module_keys,
|
||||
member_subs=payload.member_subs,
|
||||
actions=payload.actions,
|
||||
)
|
||||
snapshot = repo.get_group_binding_snapshot(group.id, group_key)
|
||||
return GroupBindingSnapshot(**snapshot)
|
||||
|
||||
|
||||
@router.post("/permission-groups", response_model=PermissionGroupItem)
|
||||
def create_permission_group(
|
||||
payload: PermissionGroupCreateRequest,
|
||||
@@ -562,11 +752,11 @@ def grant_group_permission(
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
|
||||
_resolve_module_id(db, payload.system, payload.module)
|
||||
_resolve_scope_ids(db, payload.scope_type, payload.scope_id)
|
||||
module_key = f"{payload.system}.{payload.module}" if payload.module else f"{payload.system}.__system__"
|
||||
module_name = _split_module_key(payload.module)
|
||||
row = groups_repo.grant_group_permission(
|
||||
group_id=group.id,
|
||||
system=payload.system,
|
||||
module=module_key,
|
||||
module=module_name,
|
||||
action=payload.action,
|
||||
scope_type=payload.scope_type,
|
||||
scope_id=payload.scope_id,
|
||||
@@ -587,11 +777,11 @@ def revoke_group_permission(
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
|
||||
_resolve_module_id(db, payload.system, payload.module)
|
||||
_resolve_scope_ids(db, payload.scope_type, payload.scope_id)
|
||||
module_key = f"{payload.system}.{payload.module}" if payload.module else f"{payload.system}.__system__"
|
||||
module_name = _split_module_key(payload.module)
|
||||
deleted = groups_repo.revoke_group_permission(
|
||||
group_id=group.id,
|
||||
system=payload.system,
|
||||
module=module_key,
|
||||
module=module_name,
|
||||
action=payload.action,
|
||||
scope_type=payload.scope_type,
|
||||
scope_id=payload.scope_id,
|
||||
|
||||
Reference in New Issue
Block a user