
    Ui                        d Z ddlmZ ddlmZ ddlmZ ddlmZ ddl	m
Z
  e
e          Z G d d          Z G d	 d
          ZdS )uc  Permission resolution for document access control.

Uses a unified ``acl_ids`` field with prefixed identifiers:
  A_ = area, D_ = department, O_ = office, R_ = role, U_ = user.

User tokens are built from JWT claims; the ES filter is a single ``terms``
query on the ``acl_ids`` keyword field.

文档访问权限解析模块。
基于统一前缀标识符（A_=区域, D_=部门, O_=科室, R_=角色, U_=用户）
构建用户的 ACL 令牌集合，并生成 ES 的 terms 过滤条件。
OA 系统已在每个文档的 acl_ids 中存储了完整的层级祖先 ID，
因此无需在此处进行层级展开。
    )annotations)Any)UserContext)RedisClient)
get_loggerc                  N    e Zd ZdZdZddZedd
            ZddZddZ	ddZ
dS )PermissionContextu^  Resolved permission tokens for ES query filtering.

    ``acl_tokens`` is a flat list of prefixed IDs that the user is allowed
    to match against.  For example::

        ["U_zhang_san", "O_17", "D_05", "A_01", "R_03"]

    已解析的权限上下文，包含用户的全部 ACL 令牌。
    用于构建 ES 查询中的权限过滤条件。
    user_id
acl_tokensr   strr   	list[str]c                "    || _         || _        d S Nr
   )selfr   r   s      -D:\work\zm-rag\backend\app\core\permission.py__init__zPermissionContext.__init__)   s    $    returndict[str, Any]c                     dddddiigiiS )z7Build the ES filter that matches only public documents.boolmust_notexistsfieldacl_ids r   r   r   public_es_filterz"PermissionContext.public_es_filter-   s(     h)(<=>
 	
r   c                J    d|                                  dd| j        iigddiS )zBuild the ES permission filter.

        A document is visible if:
          1. It is public (no ``acl_ids`` field), OR
          2. Any of the user's tokens appears in the document's ``acl_ids``.
        r   termsr      )shouldminimum_should_match)r   r   r   s    r   build_es_filterz!PermissionContext.build_es_filter6   sE      ))++y$/:;	 )* 

 
	
r   doc_acl_idslist[str] | Noner   c                l    |sdS t          t          |          t          | j                  z            S )zCCheck whether the current permission context can access a document.T)r   setr   )r   r&   s     r   has_acl_accessz PermissionContext.has_acl_accessI   s4     	4C$$s4?';';;<<<r   c                B    d| j         dt          | j                   dS )NzPermissionContext(user_id=z	, tokens=))r   lenr   r$   s    r   __repr__zPermissionContext.__repr__O   s6    . . .$/**. . .	
r   N)r   r   r   r   )r   r   )r&   r'   r   r   )r   r   )__name__
__module____qualname____doc__	__slots__r   staticmethodr   r%   r*   r.   r   r   r   r	   r	      s        	 	 *I% % % % 
 
 
 \

 
 
 
&= = = =
 
 
 
 
 
r   r	   c                  <    e Zd ZdZdddZdd
Zedd            ZdS )PermissionServiceu   Resolves a :class:`UserContext` into a :class:`PermissionContext`.

    权限解析服务，将 JWT 中的用户信息转换为可用于 ES 查询过滤的 ACL 令牌集合。
    支持 Redis 缓存以避免每次请求都重新构建令牌。
    Nredis_clientRedisClient | Nonec                    || _         d S r   )_redis)r   r7   s     r   r   zPermissionService.__init__]   s    "r   userr   r   r	   c                  K   | j         r|| j                             |j                   d{V }|rUd|v rQt                              d|j        t          |d                              t          |j        |d                   S |                     |          }| j         rW| j                             |j        d|i           d{V  t                              d|j        t          |                     t          |j        |          S )zBuild the full ACL token set for the user.

        1. Check Redis cache for previously resolved tokens.
        2. On miss, build tokens from JWT claims and cache them.
        Nr   perm_cache_hit)r   tokensr
   perm_cache_set)	r:   get_user_permissionsr   loggerdebugr-   r	   _build_tokensset_user_permissions)r   r;   cachedr>   s       r   resolvezPermissionService.resolve`   sQ      ; 	;;;DLIIIIIIIIF 	,&00$ Lvl344    
 ) L%l3    ##D)) ; 		+22v&         LL 6{{     !&IIIIr   r   c                   | j         g}| j        r|                    | j                   | j        r|                    | j                   | j        r|                    | j                   | j        D ]}|                    |           |S )u  Derive ACL tokens from a UserContext.

        Tokens are the user's direct identifiers at every hierarchy level,
        each prefixed by its type.  No hierarchy expansion is needed because
        the OA system already stores all ancestor IDs on each document's ACL.

        所有 ID 在 SSO/Mock 入口处已添加类型前缀（U_、O_、D_、A_、R_），
        此处直接使用，不再重复添加。
        )r   	office_idappenddept_idarea_idrole_ids)r;   r>   role_ids      r   rC   zPermissionService._build_tokens   s     "\N> 	*MM$.)))< 	(MM$,'''< 	(MM$,'''} 	# 	#GMM'""""r   r   )r7   r8   )r;   r   r   r	   )r;   r   r   r   )r/   r0   r1   r2   r   rF   r4   rC   r   r   r   r6   r6   V   sp         # # # # #$J $J $J $JL    \  r   r6   N)r2   
__future__r   typingr   app.api.depsr   app.infrastructure.redis_clientr   app.utils.loggerr   r/   rA   r	   r6   r   r   r   <module>rS      s     # " " " " "       $ $ $ $ $ $ 7 7 7 7 7 7 ' ' ' ' ' '	H		8
 8
 8
 8
 8
 8
 8
 8
vF F F F F F F F F Fr   