U
    Ef                     @   s  U d dl Z d dlZd dlZd dlZd dlmZmZ d dlmZm	Z	m
Z
mZmZmZ d dlZd dlT d dlZd dlZd dlmZ e jG dd dZe jG dd	 d	Ze jG d
d dZe ai ae	eef ed< d dddZedddhZG dd de Z!G dd dej"j#j$Z$dS )    N)OrderedDictdefaultdict)TupleDictOptionalListSetType)*)	CmfEntityc                   @   sF   e Zd ZU dZee ed< dZeed< dZee ed< dZ	eed< dS )AccessRuleDataNsubjectsobject_modelobject_fieldsaccess_level)
__name__
__module____qualname__r   r   str__annotations__r   r   r    r   r   ./cmf/models/cmf_access_list.pyr      s   
r   c                   @   sz   e Zd ZU dZeed< dZeed< dZeed< dZ	eed< dZ
eed< ejedZee ed< ejedZee ed	< dS )
AccessListDataNdisabledidcodeinherit_acl_idobject_owner_iddefault_factorycustom_rules
auto_rules)r   r   r   r   boolr   r   r   r   r   r   dataclassesfieldlistr    r   r   r!   r   r   r   r   r      s   
r   c                   @   s   e Zd ZU ejedZeee	f e
d< ejdd dZeeee f e
d< ejedZee e
d< ejedZeeeejj f e
d< ejedZeeef e
d< d	S )
AclDatar   aclc                   C   s   t tS N)r   r%   r   r   r   r   <lambda>&       zAclData.<lambda>acl_inherit_byused_fieldsstatic_access_control_modelscacheN)r   r   r   r#   r$   dictr'   r   r   r   r   r+   r   setr,   r   r-   r	   cmfmodels	BaseModelr.   r   r   r   r   r   r&   #   s
   
$$r&   _acl_cache_granted_tuplesd      )defaultreadonlyZprivatefullwritereadc                   @   s   e Zd ZdS )_AclStopN)r   r   r   r   r   r   r   r<   >   s   r<   c                       s  e Zd ZU dZdZdZeed< dZdd Z	e
dd	 Ze
d
d Ze
dAee ee ee ee ee ee eejj ee ee ee d
ddZedBddZe
dCddZedDddZedd Ze
dd Ze
dd Ze
dd Zedd  Zeed!d"dEd#d$Ze
dFd%d&Z  fd'd(Z!ed)d* Z" fd+d,Z#d-d. Z$ fd/d0Z%d1d2 Z&d3d4 Z'd5d6 Z(d7d8 Z)dG fd9d:	Z*d;d< Z+d=d> Z,e
dHd?d@Z-  Z.S )ICmfAccessList TN	_ACL_DATAi c                 C   s6   dd| j gdddgg}tjj|dD ]}|  q$d S )N	parent_id=sys_typeautofilter)r   r2   CmfAccessRuler%   delete)selfZ
acl_filterZacl_itemr   r   r   clear_auto_aclH   s    zCmfAccessList.clear_auto_aclc                 C   s4  t dt  d t }dddddh}| jdd	d
gd}|D ]R}t|j|j|j|j	|j
d}||j|j< ||j|j< |j	r>|j|j	 |j q>tjjddddgdddggddddddgd}|D ]}|jr|j|krt dt  d| d|j d|j d	 qdd |jD }|jr0dd |jD nd}	|	rF|j|	 t||jo\t|j|	t|jd}
|j|j}|st dt  d| d |j d|j d	 q|jd!kr|j|
 q|j|
 qtjj  D ]}|j!d"kr||j"|j#< q|t$_%t dt  d#t&| d$t&| d% dS )&u   
        TODO: что делаем, если загрузка acl падает??? Всё разрешаем или всё запрещаем?
        zload_acl_data(pid z): startr9   r;   r:   deny	denyWriter   r   r   fields)r   r   r   r   r   ORrA   NFr@   rB   r   r   r   r   rE   rM   z): skip z due to empty subjects(z) or invalid access_level()c                 S   s   h | ]}t |jqS r   )sysinternr   ).0subjectr   r   r   	<setcomp>n   s     z.CmfAccessList.load_acl_data.<locals>.<setcomp>c                 S   s   h | ]}t |qS r   )rQ   rR   )rS   Z
field_namer   r   r   rU   p   s     )r   r   r   r   z due to absent access_list(rC   Zstaticz
): loaded z acl, z rules)'printosgetpidr&   slistr   r   r   r   r   r   r'   r+   appendr2   rF   r   r   r   r,   updater   r   rQ   rR   getr@   rB   r!   r    r1   r3   iter_subclassesZacl_typer-   
class_namer=   r?   len)clsacl_dataZvalid_access_levelsZaccess_listsZaccess_listZaccess_list_dataZaccess_rulesZaccess_ruleZsubjects_dataZobject_fields_dataZaccess_rule_datamodelr   r   r   load_acl_dataM   sj       
$  
$zCmfAccessList.load_acl_datac                 C   s   | j }|o|jS )u   Набор полей, по которым существуют правила, чтобы не проверять всё подряд.)r?   r,   )r`   ra   r   r   r   r,      s    zCmfAccessList.used_fields)
initial_acl_keyr   object_fieldr   r   	object_idobject_instanceobject_dictobject_parent_idis_newc                    s"  d1fdd	 	
f	dd 	
fdd}dt krft t _t jd	krd
D ]}|d qxd	  d7  < d}
}|r|dg |jj|jtj	j
|dd|t jkrtj }|jj nNt j}|r|jj|jnddt jt js.t jrBd  d7  < t}|dkr,ttdr,|	spr,dr,t js,t js,d}|	r|	dr|	}rdr}|r,|   }t j|d}|dkrtjjdd|	ddsti }ti t j|< n
dt j|< n|dkr(n|}|dkr|rd}|	rV|	drV|	}tj||}|dkrpn|dkrtdh}nti }|dkr|	rr|stt dd}|dkrdd tj j!ddgdd d!gd"d#gggd$D t _"t j"}|	|krt}|dkrz| }W qW nt t#k
r } zT|j$d }t%j&'|}|dkr^ |t j(krl t )d%|  t*+d W 5 d}~X Y nX q|r||krd}|s|rt,d&t j- d'| d( d)| d* d+ d,
 d-| d. d/otj. d0 t/||S )2u  
        Проверка/получение прав доступа к объекту или его полю.
        Возвращает список доступных прав.
        Если raise_error == True, то генерирует стандартную ошибку о недостаточности прав доступа.
        Если текущий пользователь админ, или владелец объекта, или проверка прав отключена, то выдаём полные права.

        :param object_parent_id:
        TODO: object_instance может быть без филдов при sget и is_web_public
        :param object_instance:
        :param is_new:
        :param object_id:
        :param object_dict:
        :param initial_acl_key: id/code - списка доступа, по которому начинанать поиск.
        :param object_model: имя модели объекта, если пременимо
        :param object_field: поле объекта, если применимо
        :param access_level: если указан, то проверяется наличие конкретного уровня доступа.
        :param object_owner_id: владелец объекта, параметр для удобства,
            если указан и совпадает с текущим пользователем, то получаем полные права.
        :param raise_error: определяет: return False или raise CmfPermissionError
        :param checking_person - если указан, проверяются его права, а не сессионный g.current_person
        :param perm_security_level_allowed_ids - ?
        :param checked_policy - объект проверен бизнес логикой и нужно использовать указанную политику
        :return: Set[str]/False/raise CmfPermissionError
        Nc                    s^  fddfdd fddt  t  g 	z
rV
 d |r|dkrd	 d
 d n&|dkrd n|dkrn
td|trrtt}|j}|dkrn:|dkrd tn$|dkrtntd d| dr,d	 d
 d W n tk
rD   Y nX t}t	||}|S )Nc                    s   |  kr |  d S r(   )add)Zaccess_level_)deniedgrantedr   r   	add_grant   s    zBCmfAccessList.check_access.<locals>.calc_access.<locals>.add_grantc                    s   | j r| j kr| jr | jkr| j@ r| jdkrR d  d  d tn^| jdkrbtnN| jdkrd d n.| jdkr d  d n| jdkr d d S )Nr9   r:   r;   rJ   rK   )r   r   r   r   r<   rk   )rule)rn   rl   re   r   r   r   r   
check_rule   s2    






zCCmfAccessList.check_access.<locals>.calc_access.<locals>.check_rulec                    s    j | }|s>td|  d dt   td|  | |jkrltd|j d  td|  |j t	
|j|jD ]}| q|jr|jS d S )Nz.   !!!    CmfAccessList.check_access: acl_key z not found, processed z, pid=u   Не загружен ACL z0   !!!    CmfAccessList.check_access: recursion z in u+   !!! Кольцевые ссылки у ACL )r'   r\   rV   rW   rX   CmfACLNotFoundErrorr   CmfErrorrZ   	itertoolschainr    r!   r   )Zacl_keyr'   ro   )ra   	check_aclrp   processed_aclsr   r   ru      s    

zBCmfAccessList.check_access.<locals>.calc_access.<locals>.check_aclglobal)r9   r:   r9   r:   r;   rJ   z@Invalid policy value checked_policy, allowed: read, write, fulllr7   r8   zInvalid model acl policy z.acl_default_user_policy(z!), valid: default, readonly, deny)
r0   
ValueErrorr<   getattrr2   Zacl_default_user_policyrr   tupler4   
setdefault)ra   checked_policyre   Z	model_clsZmodel_policyZgranted_tuple)current_person__member_ofrd   is_local_userr   )	ra   rn   ru   rp   rl   rm   re   rv   r   r   calc_access   s^    

 
z/CmfAccessList.check_access.<locals>.calc_accessc                    s   d}r| j rd}nkr2| jr2| jkr2d}nr krP| jrP| jkrPd}nTrj| jrj| jkrjd}n:r| tjkrrddrrrjsnjjsd}t	|rdddhndh}t
||S )NFT
user_localr9   r:   r;   )Zacl_allow_createZacl_static_owner_write_fieldsZacl_static_self_write_fieldsZacl_static_user_write_fieldsr2   	CmfPersonr\   r   oldrz   r4   r{   )static_acl_modelZallow_writeresult_)	r}   current_person_idr~   rj   rh   re   rf   rg   r   r   r   calc_static.  sF    


z/CmfAccessList.check_access.<locals>.calc_staticc                     s"  d } j }|std|j}|rt	o:t	oFtoRto^tf}|j|} | d k	rd  d7  < nd  d7  < |} | d krʈrʈkrʈd  d7  < t} | d kr}|jkrd }}|srd}n
rt	
dd }to*to8t|oFt||oTt|f}|j|} | d k	rd  d7  < nd  d7  <  |||d	} | |j|< t
|jjkrt|jt
|jt|  }td
t
|j d|d  d dd t|j jd D |_| S )Nu8   Система ACL не инициализированаacl_check_cache_static   acl_check_calc_staticacl_check_owner_skipZ_acl_policyacl_check_cacheacl_check_calc)r|   re   z!CmfAccessList: shrink cache, len z, size i   z Kbc                 S   s   i | ]\}}||qS r   r   )rS   kvr   r   r   
<dictcomp>  s     zKCmfAccessList.check_access.<locals>.check_with_acl_data.<locals>.<dictcomp>   )r?   ZCmfACLNotInitializedErrorr-   r\   rQ   rR   r.   _full_accessr,   ry   r_   _CACHE_MAX_SIZE	getsizeofrV   rs   isliceitems)r   ra   r   Z	cache_keyZobject_field_Zchecked_policy_Z
cache_size)r   r   r|   r`   r   rd   rj   rh   re   rf   rg   r   r   statr   r   check_with_acl_dataG  sj    



 z7CmfAccessList.check_access.<locals>.check_with_acl_dataprofiler_data	acl_check)r   acl_check_skipr   r   r   r   r   r   r   r   Tid_onlyFr   CmfProjectPermSchemezCmfProject:.zPPP-PR-BROWSE)Zproject_id_simplecheckZobj_dict_simplecheckobjraise_errorr9   r8   r;   acl_owned_projectsc                 S   s   h | ]
}|j qS r   r   )rS   Zprojectr   r   r   rU     s   z-CmfAccessList.check_access.<locals>.<setcomp>--r   rN   cmf_owner_idrA   Zcmf_owner_assistantsINrM   rE   z2CmfAccessList.check_access(): waiting for loading z   !!!   ACL access denied for z(or z)(z), request z	, to acl=z, model=z, field=z
(requsted z), object_owner_id=(rP   )NN)0gr   r   r{   Zload_fieldsr   valuer   r2   r=   subject_full_group_listZsharelink_anonymous_userCmfPersonGroupsharelink_grouprk   Zcurrent_userr}   disable_permissionsacl_admin_moder   hasattr
startswithZcurrent_user_is_anonymousZ#current_user_is_sharelink_anonymousZproject_perm_browse_cacher\   r   Zcheck_project_role_accessrz   ZCmfSecurityLevelZcheck_perm_security_levelry   Z
CmfProjectrY   r   rq   argsREDIS_DBredis_new_acl_key
request_iddebugtimesleeprV   current_personr   CmfPermissionError)r`   rd   r   re   r   r   rf   rg   rh   ri   rj   r   Zchecking_personZperm_security_level_allowed_idsr|   r   Zstat_keyresultZrequested_object_fieldr   _userZ
project_idZppp_cache_keyZppp_resZsec_resZ_acl_owned_projectseacl_idr   r   )r   r   r|   r`   r}   r   rd   r~   rj   rh   re   rf   rg   r   r   r   r   check_access   s    )n&I










	




 PzCmfAccessList.check_accessFc                    sz   |s| r| j j}tjjj}dd|gdd|gg}dd tjj|dgdD }|t	| |rb|S  fd	d|D }|S )
NZchild_idrA   Zparent_modelr   c                 S   s   h | ]}|j rt|j qS r   )r@   rQ   rR   )rS   Zrelationr   r   r   rU   5  s   z8CmfAccessList.subject_full_group_list.<locals>.<setcomp>r@   rO   c                    s"   h | ]}t j| d  rqS )rL   )cmfutilZget_obj_by_id)rS   Zgroup_idrM   r   r   r   rU   =  s      )
r   r   r2   rF   r   ZRelationCacherY   rk   rQ   rR   )rT   
subject_idr   rM   _kwargsZgroup_model_namesZrelation_filterr   r   r   r   r   .  s    
z%CmfAccessList.subject_full_group_listc           	      K   s\   | j ||dd}tjjdgddt|gd}|s4g }|dddd	 |D gg}| j|||d
S )NT)rT   r   r   r@   r   r   r   r   c                 S   s   g | ]
}|j qS r   )r@   )rS   ro   r   r   r   
<listcomp>G  s     z2CmfAccessList.subject_list_acl.<locals>.<listcomp>)rM   rE   order_by)r   r2   rF   rY   r%   )	r`   rT   r   rM   rE   r   r   Z	member_ofZrelated_rulesr   r   r   subject_list_acl@  s    zCmfAccessList.subject_list_aclcheck_admin_modec                 C   s    t jst jrdS |rt| dS )NTF)r   r   r   r   )messager   r   r   r   r   J  s
    zCmfAccessList.check_admin_modec                   C   s:   t j jtjjjddkr*tjs*t	ddt_
dt_d S )NTr   activate_admin_mode)r2   r   Zadmin_groupr   r   r   Zrg_member_ofZall_parentsZ
is_supportr   r   r   r   r   r   r   r   S  s    z!CmfAccessList.activate_admin_modec                 C   s   dt _dt _dt _dS )u   Первоначальная инициализация контекста, чтобы работали g.current_person, в т.ч. создание пользователей.TN)r   r   r}   r   )r`   r   r   r   init_context[  s    zCmfAccessList.init_contextc                 C   s   t tddt_tjr*tjs*dt_t t_n"tjtjkt_t	j
jtjddt_tjrXdt_tjr~tjr~t	j }tj|jj dS )u\   Сейчас уже должны быть рабочие g.current_person и g.system_personr   NTr   )ry   ZAPPr   r   Zsystem_personr   r0   r}   r   r2   r=   r   Zsharelink_access_requestZsharelink_access_grantedr   r   rk   r   r   )r`   r   r   r   r   setup_contextb  s    

zCmfAccessList.setup_contextc                 C   s   t  }t  }|D ]}|| jt|dd q| j}|s<dS |j D ]2}|j|jD ] }|	|j
rV||j  qFqVqF|r| j|d dS )u  
        Нужно по списку субъектов получить список затронутых ACL и вызвать
            trigger_reload(access_list_ids)

        TODO: плохо, что ищем по текущим данным ACL, которые могут устареть.
            Можно перенести эту логику на серверный обработчик события
        T)r   r   N)access_list_ids)r0   r[   r   r   r?   r'   valuesr!   r    intersectionr   rk   r   trigger_reload)r`   Zsubjects_idsr   Zaffected_subjects_idsr   ra   r'   ro   r   r   r   subject_changed_hookx  s    	z"CmfAccessList.subject_changed_hookc                    s  t d tj  tjjdtt	
 dddrxd}t   fdd| D ]X}|d krld}t d	  qtjj|}|sqN|jd
krd}t d  q|g qN|st d|  d   tdd D ]}|rt|jd  qdd |jddgddt gddgdD }|sqt d|j dt|  t|dkrht d|j  t|jd  qt|j| qt d d S )Nzacl::proccess_chanded_acl startz!CmfAccessList::clear_jscache:lockTi N  )ZnxZpxFc                    s<   | sd S | D ]*}| krq  | tjj| qd S r(   )rk   r=   r?   r+   r\   )Zacl_id_list_Zacl_id_Zfull_idsprocess_acl_listr   r   r     s    
z<CmfAccessList.proccess_chanded_acl.<locals>.process_acl_listz8process_changed_acl(): acl_id is None, drop_all_cache!!!rw   z(process_changed_acl(): drop_all_cache!!!z#process_changed_acl(): changed_acl=z, affected_acl=c                 S   s
   t | tS r(   )
issubclassr   )mr   r   r   r)     r*   z4CmfAccessList.proccess_chanded_acl.<locals>.<lambda>c                 S   s   h | ]
}|j qS r   r   )rS   r   r   r   r   rU     s   z5CmfAccessList.proccess_chanded_acl.<locals>.<setcomp>r   r   perm_effective_acl_idr   r   e   )rM   rE   slicez3acl::proccess_chanded_acl run invalidate_ids model=z len=r5   z>acl::proccess_chanded_acl too many, flush all cache for model=zacl::proccess_chanded_acl end)r   r   r2   r=   rc   r   r   r0   r   rW   rX   r?   r'   r\   r   r   Ziter_modelsZ	CMF_CACHEZinvalidate_idsr^   rY   r%   r_   )ZidsZdrop_all_cacher   r'   rb   Zobj_idsr   r   r   proccess_chanded_acl  sJ    

	


 z"CmfAccessList.proccess_chanded_aclCmfAccessList:changed)Zchannelc                 K   sh   dd }t d | rPd| kr,t| d  qZd| krDt| d  qZtd  n
td  t| d S )Nc               	   S   sX   t d td trLt d t} t atj  tj	
|  W 5 Q R X nt d d S )Nzacl::reload handler spawned   zacl::reload handler do reloadzacl::reload handler skip)rV   r   r   _acl_changed_idsr0   r1   ZappZcmf_contextr2   r=   r   )Zchanged_aclr   r   r   handler  s    
z,CmfAccessList.on_acl_change.<locals>.handlerzacl::reload spawn handlerr   access_list_id)rV   r   r[   rk   geventZspawn)datar   r   r   r   r   on_acl_change  s    
zCmfAccessList.on_acl_changec                 C   sN   t d |d k	r(tdddd |D i |r@tddt|i n
tdd  d S )Nzacl::reload triggerr   r   c                 S   s   g | ]}t |qS r   )r   )rS   r   r   r   r   r     s     z0CmfAccessList.trigger_reload.<locals>.<listcomp>r   )rV   Zcmf_emit_server_eventr   )r`   r   r   r   r   r   r     s    zCmfAccessList.trigger_reloadc                    s   t   dddg S )Nr   Zpolicyparent)supersave_preload_fieldsrH   	__class__r   r   r     s    z!CmfAccessList.save_preload_fieldsc                 C   s
   d|  S )NzCmfAccessList::created:r   )r   r   r   r   r      s    zCmfAccessList._new_acl_keyc                    s<   |  | j | jr.tjj| | jtjdd t	 j
f |S )N
   )ex)r   r   rj   r   r   r0   r   r   r   r   save)rH   kwargsr   r   r   r     s    zCmfAccessList.savec                 K   s   g }t jj D ]F}||jdddddddgddd	| jgdd	| jgdd	| jggd
 q|| jddddddgdd	| jgd
 |S )Nr   r   r   namer   Zperm_inherit_acl_idZperm_acl_idrN   rA   r   r   )r1   r2   r   r]   extendrY   r   )rH   r   r   rb   r   r   r   find_acl_usage  s    




zCmfAccessList.find_acl_usagec              	      sL   |   }|r2td|  dt| d|d d  | | j t jf |S )Nu)   Не возможно удалить acl u,   , т.к. на него существует u    ссылок, r   )r   ZCmfOrmIntegrityErrorr_   r   r   r   rG   )rH   r   usager   r   r   rG     s     zCmfAccessList.deletec                 C   s   d S r(   r   r   r   r   r   _calc_perm_parent%  s    zCmfAccessList._calc_perm_parentc                 C   s   d S r(   r   r   r   r   r   _calc_perm_has_acl(  s    z CmfAccessList._calc_perm_has_aclc                 K   s   d S r(   r   )rH   r   r   r   r   _calc_perm_acl+  s    zCmfAccessList._calc_perm_aclc                 C   s   d S r(   r   r   r   r   r   _calc_perm_effective_acl.  s    z&CmfAccessList._calc_perm_effective_aclc                    s   |st jg}t j|dS )N)spread_models)r2   rF   r   _acl_spread_inheritance)rH   r   r   r   r   r   1  s    z%CmfAccessList._acl_spread_inheritancec                 C   s2   |    | jr d| _| jdd tj| j d S )NFTZ	only_data)save_preparer   r   r2   r=   r   r   )rH   Z_ruler   r   r   save_rule_hook6  s
    zCmfAccessList.save_rule_hookc                 C   sR   |    | js@tjjdd| gdd|jggds@d| _| jdd tj| j d S )Nr   rA   r   z!=rD   Tr   )	r   r   r2   rF   rY   r   r   r=   r   )rH   ro   r   r   r   delete_rule_hook=  s    zCmfAccessList.delete_rule_hookc                 C   s   | j ddrdS dd |jddD }|s.dS tj }|sBtd|r`|jr`tj|_tj|_dS |rp|t	|8 }|j
jtjjkr|r|t	|8 }|jtjkr|r|t	|8 }|sdS | j d| d	| d
 dS )z)deprecated method, due not API integratedF)r   Tc                 S   s   h | ]}|j s|jqS r   )Zno_aclr^   )rS   r$   r   r   r   rU   N  s   z:CmfAccessList.subject_model_check_write.<locals>.<setcomp>)Z
is_changedzAccess Prohibitedztry change fields z on )r   N)r   r   r   Zpublic_accessr   rj   r   Z	cmf_ownerZ
cmf_authorr0   r   r   r   r}   )r`   rT   Zallow_createZallow_user_fieldsZallow_owner_fieldsZallow_self_fieldsZchanged_fieldsZis_userr   r   r   subject_model_check_writeF  s.    

z'CmfAccessList.subject_model_check_write)NNNNNNNNNNTNNN)NNFN)NNNNN)r   T)N)NN)N)FNNN)/r   r   r   __doc__ZTEXKOM_no_cacher?   r&   r   r   rI   classmethodrc   r,   r   r   r1   r2   r3   r/   r"   r   staticmethodr   r   r   r   r   r   r   r   Zon_server_eventr   r   r   r   r   r   rG   r   r   r   r   r   r   r   r   __classcell__r   r   r   r   r=   B   s   

C
              
   	




@

	       r=   )%r#   rs   rQ   r   collectionsr   r   typingr   r   r   r   r   r	   r   Zcmf.includeZcmf.appr1   Zcmf.fields.cmf_access_listZ
cmf.modelsr   Z	dataclassr   r   r&   r0   r   r4   r   Z_policy_prioritiesrz   r   	Exceptionr<   rM   Zcmf_access_listr=   r   r   r   r   <module>   s4     
