o
    =)j%                     @  s   d Z ddlmZ ddlZddlmZmZmZmZm	Z	 ddl
mZ eddhZeh dZed	d
hZd:ddZd;ddZd<ddZddd=dd Zddd>d"d#Zd?d$d%Zd@d'd(ZdAd)d*Zd+d,dBd/d0ZdCd2d3Z	dDdEd5d6ZdFd8d9ZdS )Gu  
Role + scope helpers for FastAPI.

Design: additive / non-breaking rollout
- Master admin, business admin, admin, and user roles keep full business access (current behavior).
- Manager/agent scopes apply ONLY when explicit mappings exist in user_group_mapping /
  user_agent_mapping; otherwise those roles also see full business data (migration-safe).
- Set RBAC_STRICT=1 to always enforce manager/agent scopes (even with empty mappings → no rows).
    )annotationsN)AnyDictListOptionalTuple)AuthHandleradminbusiness_admin>   userr	   r
   manageragentreturnboolc                   C  s   t dd  dv S )NRBAC_STRICT )1trueyeson)osgetenvstriplower r   r   //home/aiteam/pcaa-dev/dashboard-backend/rbac.pyrbac_strict   s   r   roleOptional[str]strc                 C  s
   t | S N)r   normalize_role)r   r   r   r   r!      s   
r!   r   Dict[str, Any]bidOptional[Dict[str, Any]]c                 C  s`   | sd S |  drt|ddgddidS |  dpg D ]}t| dt|kr-|  S qd S )	N	is_masterr	   *typeallr#   r   permissionsscope
businessesr#   )getr   )r   r#   businessr   r   r   get_business_entry   s   
r/   Freloadauth_handlerr   r1   c                  s"  |  drt ddgddidS t|  }|s#t dg ddidS |  d	}|rm|rm||p2g }t fd
d|D d}|rmt| d}t| dpT|| |}	t| dpb|	| |}
t ||	|
dS t| d}t| dp{g }	t| dpddi}
t ||	|
dS )zQReturn role, permissions, scope for one business (reload from DB when requested).r%   r	   r&   r'   r(   r)   r   noneidc                 3  s*    | ]}t |d t  kr|V  qdS )r#   N)r   r-   ).0br#   r   r   	<genexpr>A   s   ( z+resolve_business_context.<locals>.<genexpr>Nr   r*   r+   r.   )
r-   r   r/   get_user_businessesnextr!   listget_permissions_for_userdictget_user_scope)r   r#   r2   r1   entryuser_idr,   db_entryr   r*   r+   r   r7   r   resolve_business_context*   s,   


rB   
permissionc                C  sD   |  drdS t| |||d}| dpg }d|v rdS t||v S )Nr%   Tr0   r*   r&   )r-   rB   r   )r   r#   rC   r2   r1   ctxpermsr   r   r   has_permissionO   s   
rF   c                 C  s2   |  drdS t| |}|sdS t| dtv S )Nr%   TFr   )r-   r/   r!   ADMIN_ROLES)r   r#   r?   r   r   r   is_business_admin`   s   

rH   r+   c                 C  s   | sdS t | dpd }|dkrMdd | dpg D }dd | d	p(g D }d
d | dp4g D }dd | dp@g D }t|pK|pK|pK|S |dkr}dd | d	pZg D }dd | dpfg D }dd | dprg D }t|p{|p{|S dS )NFr'   r   teamc                 S     g | ]}|r|qS r   r   r5   gr   r   r   
<listcomp>n       z&scope_has_mappings.<locals>.<listcomp>
groupnamesc                 S  rJ   r   r   r5   ar   r   r   rM   o   rN   agent_namesc                 S  rJ   r   r   r5   pr   r   r   rM   p   rN   agent_phonesc                 S  rJ   r   r   r5   er   r   r   rM   q   rN   agent_extensions	own_agentc                 S  rJ   r   r   rP   r   r   r   rM   t   rN   c                 S  rJ   r   r   rS   r   r   r   rM   u   rN   c                 S  rJ   r   r   rV   r   r   r   rM   v   rN   )r   r-   r   r   )r+   
scope_typegroupsagentsphonesextsr   r   r   scope_has_mappingsi   s   r_   c                 C  sj   |  drdS t| ||t|  dd}| d}|tv rdS |tvr%dS | dp+i }t r1dS t|S )z7True when SQL/row filters should restrict visible data.r%   Fr4   r0   r   r+   T)r-   rB   r   FULL_ACCESS_ROLESSCOPED_ROLESr   r_   )r   r#   r2   rD   r   r+   r   r   r   should_apply_data_scope{   s   

rb   r)aliasrd   Tuple[str, List[Any]]c                  sh  t | dpd }|dv r|dkrdg fS dg fS g g d# fdd}|dkrqdd | dp5g D }|rUddgt| }d  d| d | |d| dp]g  |d| dpgg | dpmg   n|dkr|d| dp}g  |d| dpg | dpg   st rdg fS dg fS tdkrd fS d d! d fS )$zx
    Build AND (...) SQL fragment for raw_calls row filter.
    Returns ("", []) when scope is business-wide / all.
    r'   r.   )r(   r.   r3   r3   z1=0r   columnr   values	List[Any]r   Nonec              	     s`   dd |D }|sd S d dgt| }d  d|  d| d d	d |D  d S )
Nc                 S  rJ   r   r   r5   vr   r   r   rM      rN   z@build_raw_call_scope_sql.<locals>._in_clause.<locals>.<listcomp>, %s
TRIM(CAST(.z AS CHAR)) IN ()c                 S  s   g | ]}t | qS r   r   r   rj   r   r   r   rM      s    )joinlenappendextend)rf   rg   cleanplaceholdersrd   paramspartsr   r   
_in_clause   s   z,build_raw_call_scope_sql.<locals>._in_clauserI   c                 S  rJ   r   r   rK   r   r   r   rM      rN   z,build_raw_call_scope_sql.<locals>.<listcomp>rO   rl   rm   rn   z.groupname AS CHAR)) IN (rp   	agentnamerR   agent_callinforU   rX   rY      r   (z OR N)rf   r   rg   rh   r   ri   )r   r-   r   rr   rs   rt   ru   r   )r+   rd   rZ   r{   r[   rw   r   rx   r   build_raw_call_scope_sql   s4   	
$"r   rowc                 C  s  | sdS t |dpd }|dv rdS |dkrdS t | dp'| dp'd	 }t | d
p2d	 }t | dp=d	 }|dkrdd |dpNg D }dd |dpZg D }dd |dpfg D }dd |dprg D }	||	B }
|r||v rdS |r||v rdS |
r||
v rdS dS |dkrdd |dpg D }dd |dpg D }dd |dpg D }	||	B }
|r||v rdS |
r||
v rdS dS dS )NFr'   r.   )r(   r.   Tr3   r|   
agent_namer   r}   	groupnamerI   c                 S     h | ]
}|rt | qS r   rq   rK   r   r   r   	<setcomp>       z'call_record_in_scope.<locals>.<setcomp>rO   c                 S  r   r   rq   r5   nr   r   r   r      r   rR   c                 S  r   r   rq   rS   r   r   r   r      r   rU   c                 S  r   r   rq   rV   r   r   r   r      r   rX   rY   c                 S  r   r   rq   r   r   r   r   r      r   c                 S  r   r   rq   rS   r   r   r   r      r   c                 S  r   r   rq   rV   r   r   r   r      r   )r   r-   r   r   )r   r+   rZ   r   r}   r   r[   namesr]   r^   identifiersr   r   r   call_record_in_scope   sD    r   requested_agent_namesc                 C  s\   |r|S t | ||sdS t| ||dddpi }dd |dp"g D }|s)dS d|S )	zNReturn comma-separated agent names when scope should default the agent filter.NTr0   r+   c                 S  s   g | ]
}|rt | qS r   rq   r   r   r   r   rM      r   z3effective_agent_names_for_scope.<locals>.<listcomp>rR   ,)rb   rB   r-   rr   )r   r#   r2   r   r+   r   r   r   r   effective_agent_names_for_scope   s   
r   requested_groupnamec                 C  s|   |r|S t | ||sdS t| ||}|dpi }t|d dkr&dS dd |dp/g D }t|dkr<|d	 S dS )
z
    For managers with a single assigned group and no explicit filter, default to that group.
    Otherwise pass through requested_groupname unchanged.
    Nr+   r'   rI   c                 S  rJ   r   r   rK   r   r   r   rM     rN   z1effective_groupname_for_scope.<locals>.<listcomp>rO   r~   r   )rb   rB   r-   r   r   rs   )r   r#   r2   r   rD   r+   r[   r   r   r   effective_groupname_for_scope   s   
r   )r   r   )r   r   r   r   )r   r"   r#   r   r   r$   )
r   r"   r#   r   r2   r   r1   r   r   r"   )r   r"   r#   r   rC   r   r2   r   r1   r   r   r   )r   r"   r#   r   r   r   )r+   r"   r   r   )r   r"   r#   r   r2   r   r   r   )r+   r"   rd   r   r   re   )r   r"   r+   r"   r   r   r    )
r   r"   r#   r   r2   r   r   r   r   r   )
r   r"   r#   r   r2   r   r   r   r   r   )__doc__
__future__r   r   typingr   r   r   r   r   r2   r   	frozensetrG   r`   ra   r   r!   r/   rB   rF   rH   r_   rb   r   r   r   r   r   r   r   r   <module>   s0    



+

	

0-