o
    ?7ih>                     @   s  U d Z ddlZddlmZ ddlmZ ddlZddlZddlZddl	m
Z
 ddlmZ ddlmZ eeZedi d	Zee ed
< daeej ed< daeej ed< dejfddZdejfddZ	dCdedededededee fddZdededededef
ddZdDdeded efd!d"Z dededededef
d#d$Z!dedededefd%d&Z"dedededefd'd(Z#dedede$e fd)d*Z%dedede$e fd+d,Z&dedede$e fd-d.Z'dededededef
d/d0Z(dededededef
d1d2Z)dedede$e$e  fd3d4Z*defd5d6Z+defd7d8Z,dededdfd9d:Z-dededdfd;d<Z.defd=d>Z/defd?d@Z0de$e fdAdBZ1dS )Ez#
Casbin RBAC Permission Management
    N)Adapter)Optional)settings)SessionLocal)
ContextVarrequest_context)default_request_context	_enforcer_redis_clientreturnc                   C   sB   t du rtjrtjtjdda t S tjtjtjtjtj	dda t S )zGet or create Redis clientNT)decode_responses)hostportdbpasswordr   )
r   r   	REDIS_URLredisfrom_urlRedis
REDIS_HOST
REDIS_PORTREDIS_DBREDIS_PASSWORD r   r   </lsinfo/ai/hellotax_ai/base_platform/app/core/permissions.pyget_redis_client   s   r   c               	   C   s   t du rwtjtjtjtjt} tj| dd}ttj	}t
||a dtdtdtfdd}d	td
tdtfdd}dtdtdtfdd}t d| t d| t d| td td|  tdtj	dd   t S )zc
    Get or create the Casbin enforcer instance

    Returns:
        Casbin enforcer instance
    Ncasbinz
model.confdomain1domain2r   c                 S      | |kp|dkS N*r   )r   r   r   r   r   domain_match:      z"get_enforcer.<locals>.domain_match	resource1	resource2c                 S   r    r!   r   )r%   r&   r   r   r   resource_match=   r$   z$get_enforcer.<locals>.resource_matchaction1action2c                 S   r    r!   r   )r(   r)   r   r   r   action_match@   r$   z"get_enforcer.<locals>.action_matchdomainMatchresourceMatchactionMatchu5   ✅ Casbin enforcer initialized with database adapterz
   Model: z   Database: @)r
   ospathdirnameabspath__file__joinr   r   DATABASE_URLr   Enforcerstrbooladd_functionloggerinfosplit)base_dir
model_pathadapterr#   r'   r*   r   r   r   get_enforcer%   s   	$

rA   user_id	tenant_idresourceactionresultrole_idc           	      C   s   z$ddl m} t }|j| ||||||d|d|dd	 W dS  ty? } ztd|  W Y d}~dS d}~ww )	z+Log permission check to audit table (async)r   )log_permission_check
ip_address
user_agentr1   )	rB   rC   rD   rE   rF   rG   rI   rJ   request_pathz&Failed to queue permission check log: N)app.tasks.audit_tasksrH   r	   getdelay	Exceptionr;   error)	rB   rC   rD   rE   rF   rG   rH   ctxer   r   r   _log_permission_checkN   s$   	rS   action_typerolec                 C   s   z(ddl m} t }|j| |||||dd|d|d|dd	 W d	S  tyC } ztd|  W Y d	}~d	S d	}~ww )
z2Log permission grant/revoke to audit table (async)r   )log_permission_changerB   rI   rJ   r1   )	rT   rC   rD   rE   rU   rB   rI   rJ   rK   z'Failed to queue permission change log: N)rL   rV   r	   rM   rN   rO   r;   rP   )rT   rC   rD   rE   rU   rV   rQ   rR   r   r   r   _log_permission_changej   s$   
rW   rI   rJ   r1   c                 C   s   t | ||d dS )z%Set request context for audit loggingrI   rJ   r1   N)r	   setrX   r   r   r   set_request_context   s
   rZ   c                 C   s  d|  d| d| d| }zt  }||}|dur |dkW S W n ty; } ztd|  W Y d}~nd}~ww t }d|  }	t|}
||	|
||}td|  d| d	| d
| d| 
 z|	|d|rmdnd W |S  ty } ztd|  W Y d}~|S d}~ww )a^  
    Check if user has permission to perform action on resource

    Args:
        user_id: User ID
        tenant_id: Tenant ID (domain)
        resource: Resource name (e.g., "knowledge_bases", "agents")
        action: Action name (e.g., "read", "create", "update", "delete")

    Returns:
        True if user has permission, False otherwise
    perm::N1zRedis cache error: user:zPermission check: user=	, tenant=, resource=	, action=z -> i,  0zRedis cache write error: )
r   rM   rO   r;   warningrA   r8   enforcedebugsetex)rB   rC   rD   rE   	cache_keyredis_clientcachedrR   enforcersubjectdomainrF   r   r   r   check_permission   s@   


rm   c                 C   s  ddl m} ddlm} ddlm} t }z|||j	| k
 }|s-td|  d|||j	|k
 }|sDtd| d|jdurX|j|krXtd|  d	| d
|v rc|d
d n|}	|||j|	k
 }
|
s|td|	 d|
jr|jdur|	dv rtd|	 d|
jdur|
j|krtd|	 d	| t }d|  }t|}||||}|r|  td| d|  d|  t| | ntd| d|  d|  |W |  S |  w )a  
    Add a role for user in a specific tenant with validation

    Args:
        user_id: User ID
        role: Role name (e.g., "role:customer_admin")
        tenant_id: Tenant ID

    Returns:
        True if successful

    Raises:
        ValueError: If validation fails
    r   )User)Tenant)RolezUser z does not existzTenant Nz does not belong to tenant r\   r/   zRole >   platform_userplatform_adminzCannot assign platform role z to tenant userr^   u   ✅ Added role z
 for user  in tenant u   ⚠️ Failed to add role )app.models.userrn   app.models.tenantro   app.models.rolerp   r   queryfilteridfirst
ValueErrorrC   r=   code	is_systemrA   r8   add_grouping_policyload_policyr;   r<   invalidate_user_permissionsrc   close)rB   rU   rC   rn   ro   rp   r   usertenant	role_coderole_objrj   rk   rl   successr   r   r   add_role_for_user   s@   
r   c                 C   s\   t  }d|  }t|}||||}|r,|  td| d|  d|  t| | |S )z
    Remove a role from user in a specific tenant

    Args:
        user_id: User ID
        role: Role name
        tenant_id: Tenant ID

    Returns:
        True if successful
    r^   u   ✅ Removed role z from user rs   )rA   r8   remove_grouping_policyr   r;   r<   r   )rB   rU   rC   rj   rk   rl   r   r   r   r   remove_role_for_user
  s   

r   c                 C   s(   t  }d|  }t|}|||}|S )z
    Get all roles for user in a specific tenant

    Args:
        user_id: User ID
        tenant_id: Tenant ID

    Returns:
        List of role names
    r^   )rA   r8   get_roles_for_user_in_domain)rB   rC   rj   rk   rl   rolesr   r   r   get_roles_for_user)  s
   
r   c                 C   s   t | |}dd |D S )z
    Get user roles in format expected by API permissions

    Args:
        user_id: User ID
        tenant_id: Tenant ID

    Returns:
        List of dicts with role_code: [{'role_code': 'platform_admin'}, ...]
    c                 S   s   g | ]
}d | ddiqS )r   zrole: )replace).0rU   r   r   r   
<listcomp>N  s    z"get_user_roles.<locals>.<listcomp>)r   )rB   rC   r   r   r   r   get_user_rolesB  s   
r   c                 C   s   t  }t|}|| |}|S )z
    Get all users with a specific role in a tenant

    Args:
        role: Role name
        tenant_id: Tenant ID

    Returns:
        List of user IDs (as strings with "user:" prefix)
    )rA   r8   get_users_for_role_in_domain)rU   rC   rj   rl   usersr   r   r   get_users_for_roleQ  s   r   c              
   C   j   t  }t|}|| |||}|r3|  td|  d| d| d|  td||||  t| | |S )z
    Add a permission for a role in a specific tenant

    Args:
        role: Role name
        tenant_id: Tenant ID
        resource: Resource name
        action: Action name

    Returns:
        True if successful
    u   ✅ Added permission: role=r_   r`   ra   grant)rA   r8   
add_policyr   r;   r<   rW   _invalidate_role_permissionsrU   rC   rD   rE   rj   rl   r   r   r   r   add_permission_for_rolei  s   
r   c              
   C   r   )z
    Remove a permission from a role in a specific tenant

    Args:
        role: Role name
        tenant_id: Tenant ID
        resource: Resource name
        action: Action name

    Returns:
        True if successful
    u   ✅ Removed permission: role=r_   r`   ra   revoke)rA   r8   remove_policyr   r;   r<   rW   r   r   r   r   r   remove_permission_for_role  s   
r   c                 C   s    t  }t|}|d| |}|S )z
    Get all permissions for a role in a specific tenant

    Args:
        role: Role name
        tenant_id: Tenant ID

    Returns:
        List of permissions (each permission is a list: [role, domain, resource, action])
    r   )rA   r8   get_filtered_policy)rU   rC   rj   rl   permissionsr   r   r   get_permissions_for_role  s   r   c                  C      t  } |  S )zN
    Save current policy to file

    Returns:
        True if successful
    )rA   save_policyrj   r   r   r   r        r   c                  C   r   )zJ
    Reload policy from file

    Returns:
        True if successful
    )rA   r   r   r   r   r   r     r   r   c              
   C   s   z+t  }d|  d| d}||}|r)|j|  tdt| d|   W dS W dS  tyF } ztd|  W Y d}~dS d}~ww )zs
    Invalidate cached permissions for a user

    Args:
        user_id: User ID
        tenant_id: Tenant ID
    r[   r\   z:*zInvalidated z cached permissions for user z(Failed to invalidate permissions cache: N)r   keysdeleter;   r<   lenrO   rc   )rB   rC   rh   patternr   rR   r   r   r   r     s   

 r   c              
   C   sx   z t | |}|D ]}|drt|dd }t|| qW dS  ty; } ztd|  W Y d}~dS d}~ww )z
    Invalidate cached permissions for all users with a specific role

    Args:
        role: Role name
        tenant_id: Tenant ID
    r^   r\      z'Failed to invalidate role permissions: N)r   
startswithintr=   r   rO   r;   rc   )rU   rC   r   user_subjectrB   rR   r   r   r   r     s   


r   c                 C   sJ   | j dv rdS | j dkr|j| jkS | j dkr#|j| jkp"|j| jkS dS )zDCheck if user has permission to access a knowledge base (deprecated)rr   rq   Tcustomer_admincustomer_userF)rU   rC   
created_byry   )r   kbr   r   r   can_access_knowledge_base	  s   


r   c                 C   s>   | j dv rdS | j dkr|j| jkS | j dkr|j| jkS dS )z>Check if user has permission to access a document (deprecated)r   Tr   r   F)rU   rC   )r   docr   r   r   can_access_document  s   


r   c                 C   s   ddl m} ddlm} |jdv rdd | |j D S |jdkr7dd | |j|j	|j	k D S |jd	krWd
d | |j||j
|jk|j	|j	k D S g S )z@Get list of knowledge base IDs that user can access (deprecated)r   )KnowledgeBase)or_r   c                 S      g | ]}|j qS r   ry   r   r   r   r   r   r   %      z2get_accessible_knowledge_bases.<locals>.<listcomp>r   c                 S   r   r   r   r   r   r   r   r   '  r   r   c                 S   r   r   r   r   r   r   r   r   +  r   )app.models.knowledge_baser   
sqlalchemyr   rU   rw   ry   allrx   rC   r   )r   r   r   r   r   r   r   get_accessible_knowledge_bases  s(   





r   )N)NNN)2__doc__r   casbin_sqlalchemy_adapterr   typingr   r0   loggingr   
app.configr   app.db.sessionr   contextvarsr   	getLogger__name__r;   r	   dict__annotations__r
   r7   r   r   r   rA   r   r8   r9   rS   rW   rZ   rm   r   r   listr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s    
/

	
3
I



&
$

