bmc_hub/app/modules/sag/backend/visibility.py

87 lines
4.5 KiB
Python

"""Explicit case access lists supplement the existing general case permissions."""
from fastapi import APIRouter,Depends,HTTPException
from pydantic import BaseModel,Field
from app.core.auth_dependencies import get_current_user
from app.core.case_visibility import require_case
from app.core.database import atomic_transaction,execute_query,execute_query_single
from app.modules.planner.backend.work import audit
router=APIRouter()
def can_manage(user):
return bool(user.get('is_superadmin') or 'cases.manage_confidential' in set(user.get('permissions') or []))
def require_read(user,sag_id):
if not (user.get('is_superadmin') or set(user.get('permissions') or []) & {'cases.view','tickets.view'}):
raise HTTPException(403,'Du har ikke adgang til sager')
require_case(user,sag_id)
if not execute_query_single('SELECT id FROM sag_sager WHERE id=%s AND deleted_at IS NULL',(sag_id,)):
raise HTTPException(404,'Sagen findes ikke')
def state(sag_id):
row=execute_query_single('SELECT version,restricted FROM sag_case_visibility WHERE sag_id=%s',(sag_id,))
return {'restricted':row['restricted'] if row else False,'version':row['version'] if row else 0,
'user_ids':[r['user_id'] for r in execute_query('SELECT user_id FROM sag_case_visibility_users WHERE sag_id=%s',(sag_id,)) or []],
'group_ids':[r['group_id'] for r in execute_query('SELECT group_id FROM sag_case_visibility_groups WHERE sag_id=%s',(sag_id,)) or []]}
@router.get('/sag/{sag_id}/visibility')
def get_visibility(sag_id:int,user:dict=Depends(get_current_user)):
require_read(user,sag_id)
result=state(sag_id)
result['can_manage']=can_manage(user)
if result['can_manage']:
result['users']=execute_query('SELECT user_id AS id,COALESCE(full_name,username) AS name FROM users WHERE is_active ORDER BY name') or []
result['groups']=execute_query('SELECT id,name FROM groups ORDER BY name') or []
else:
result.pop('user_ids')
result.pop('group_ids')
return result
class VisibilityInput(BaseModel):
restricted:bool
version:int=Field(ge=0)
user_ids:list[int]=Field(default_factory=list,max_length=1000)
group_ids:list[int]=Field(default_factory=list,max_length=1000)
@router.put('/sag/{sag_id}/visibility')
def update_visibility(sag_id:int,payload:VisibilityInput,user:dict=Depends(get_current_user)):
require_read(user,sag_id)
if not can_manage(user):
raise HTTPException(403,'Du må ikke ændre fortrolighed')
ids,groups=sorted(set(payload.user_ids)),sorted(set(payload.group_ids))
if payload.restricted and not ids and not groups:
raise HTTPException(400,'Vælg mindst én medarbejder eller gruppe')
if payload.restricted and not user.get('is_superadmin') and user['id'] not in ids:
own_groups=execute_query('SELECT group_id FROM user_groups WHERE user_id=%s AND group_id=ANY(%s)',(user['id'],groups)) or []
if not own_groups:
raise HTTPException(400,'Du skal bevare din egen adgang til sagen')
with atomic_transaction():
execute_query('SELECT id FROM sag_sager WHERE id=%s FOR UPDATE',(sag_id,))
old=state(sag_id)
if old['version']!=payload.version:
raise HTTPException(409,'Adgangen er ændret. Genindlæs.')
if len(execute_query('SELECT user_id FROM users WHERE user_id=ANY(%s) AND is_active',(ids,)) or [])!=len(ids):
raise HTTPException(400,'Ukendt eller inaktiv medarbejder')
if len(execute_query('SELECT id FROM groups WHERE id=ANY(%s)',(groups,)) or [])!=len(groups):
raise HTTPException(400,'Ukendt gruppe')
execute_query('''INSERT INTO sag_case_visibility(sag_id,updated_by,restricted) VALUES(%s,%s,%s)
ON CONFLICT(sag_id) DO UPDATE SET version=sag_case_visibility.version+1,restricted=EXCLUDED.restricted,
updated_by=EXCLUDED.updated_by,updated_at=NOW()''',(sag_id,user['id'],payload.restricted),fetch=False)
execute_query('DELETE FROM sag_case_visibility_users WHERE sag_id=%s',(sag_id,),fetch=False)
execute_query('DELETE FROM sag_case_visibility_groups WHERE sag_id=%s',(sag_id,),fetch=False)
if payload.restricted:
for uid in ids:
execute_query('INSERT INTO sag_case_visibility_users(sag_id,user_id) VALUES(%s,%s)',(sag_id,uid),fetch=False)
for gid in groups:
execute_query('INSERT INTO sag_case_visibility_groups(sag_id,group_id) VALUES(%s,%s)',(sag_id,gid),fetch=False)
new=state(sag_id)
audit(user,'visibility_update','case',sag_id,old,new,sag_id)
return new