U
    !]h{                     @   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 Z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e	j
jjjdg Zdd Zed	d
 Zedd ZedDe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dEddZedFddZedGddZedd Zedd ZdZedd Z edd Z!ee"dd gd!dd"d#dHd$d%Z#ee$d&d'dId(d)Z%edJd*d+Z& fd,d-Z'ed.d/ Z( fd0d1Z)d2d3 Z* fd4d5Z+d6d7 Z,d8d9 Z-d:d; Z.d<d= Z/dK fd>d?	Z0d@dA Z1dBdC Z2  Z3S )LCmfAccessList TN	_ACL_DATAi subject_list_aclc                 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_aclK   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   ORrB   NFrA   rC   r   r   r   r   rF   rN   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>q   s     z.CmfAccessList.load_acl_data.<locals>.<setcomp>c                 S   s   h | ]}t |qS r   )rR   rS   )rT   Z
field_namer   r   r   rV   s   s     )r   r   r   r   z due to absent access_list(rD   Zstaticz
): loaded z acl, z rules)'printosgetpidr&   slistr   r   r   r   r   r   r'   r+   appendr2   rG   r   r   r   r,   updater   r   rR   rS   getrA   rC   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_dataP   sj       
$  
$zCmfAccessList.load_acl_datac                 C   s   | j }|o|jS )u   Набор полей, по которым существуют правила, чтобы не проверять всё подряд.)r?   r,   )ra   rb   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  t j}d>fdd	 	
f
dd 	
fdd}d|krvt t _t jn|d d	krd
D ]}|d qd	  d7  < d}}rdg jjjt	j
jddt jk}t jkrpt	j }|jj n\t j}|r0|jj|jndd|d |d }|d s\|d rpd  d7  < t}|dkrtt	dr|	srdr|s|d sd}|	r|	dr|	}rވdrވ}|r|   }
ss|  }| t j }|d |d}|dkrt	jjdd|
ddst jdkr~t	j
jd|	
|t jd
sti }ti t j|< n
dt j|< n|dkrn|}|dkrtt	drrdr|st jsd}
r
d }njjrj }|r|   }t j|d}|dkrt jjkr~t	jjdd!|
dds~ti }ti t j|< n
dt j|< n|dkrn|}|dkr|rd}|	r|	dr|	}t	j !||}|d"krn|d#krtd$h}nti }|dkrt|	rtrtstt"t d%d}|dkrfd&d' t	j#j$d(d)gd*d+d,gd-d.gggd/D }|t _%|	|krtt}|dkrz| }W qW nx t&k
r } zX|j'd }t(j)*|}|dkrʂ ||d0 krڂ |d1 d2|  t+,d W 5 d}~X Y nX qt|r ||kr d}|s|rt-d3t j. d4 d5 d6| d7 d8 d9 d:| d; d<oxt	j/ d= t0||S )?u  
        Проверка/получение прав доступа к объекту или его полю.
        Возвращает список доступных прав.
        Если 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;   rK   rL   )r   r   r   r   r<   rl   )rule)ro   rm   rf   r   r   r   r   
check_rule   s2    






zCCmfAccessList.check_access.<locals>.calc_access.<locals>.check_rulec                    s    j | }|sL| tjkrd S td|  d dt   td|  | |jkrzt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]   gZ
new_acl_idrW   rX   rY   CmfACLNotFoundErrorr   CmfErrorr[   	itertoolschainr    r!   r   )Zacl_keyr'   rp   )rb   	check_aclrq   processed_aclsr   r   rw      s     


zBCmfAccessList.check_access.<locals>.calc_access.<locals>.check_aclglobal)r9   r:   r9   r:   r;   rK   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_policyrt   tupler4   
setdefault)rb   checked_policyrf   Z	model_clsZmodel_policyZgranted_tuple)current_person__member_ofre   is_local_userr   )	rb   ro   rw   rq   rm   rn   rf   rx   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}nkrP| jrP| jkrPd}nnrj| jrj| jkrjd}nTr| tjkrrddrrrjsnjjs }|st	j
}|jddrd}t|rdddhndh}t||S )	NFT
user_localZContactAdmins)Z
group_coder9   r:   r;   )Zacl_allow_createZacl_static_owner_write_fieldsZacl_static_self_write_fieldsZacl_static_user_write_fieldsr2   	CmfPersonr]   r   oldrr   current_userZin_person_groupr|   r4   r}   )static_acl_modelZallow_writeZ_chk_personresult_)
checking_personr   current_person_idr   rk   ri   rf   rg   rh   r   r   r   calc_static4  sN    


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~   rf   z!CmfAccessList: shrink cache, len z, size i   z Kbc                 S   s   i | ]\}}||qS r   r   )rT   kvr   r   r   
<dictcomp>  s     zKCmfAccessList.check_access.<locals>.check_with_acl_data.<locals>.<dictcomp>   )r?   ZCmfACLNotInitializedErrorr-   r]   rR   rS   r.   _full_accessr,   r{   r`   _CACHE_MAX_SIZE	getsizeofrW   ru   isliceitems)r   rb   r   Z	cache_keyZobject_field_Zchecked_policy_Z
cache_size)r   r   r~   ra   r   re   rk   ri   rf   rg   rh   r   r   statr   r   check_with_acl_dataQ  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   current_user_is_anonymousdisable_permissionsacl_admin_moder   CmfProjectPermSchemezCmfProject:#current_user_is_sharelink_anonymousproject_perm_browse_cache.zPPP-PR-BROWSE)Zproject_id_simplecheckZobj_dict_simplecheckobjraise_errorZpub_api)
re   r   r   r   rg   rj   ri   perm_security_level_allowed_idsr~   r   zCmfTimeTrackerHistory:
project_idzPPP-WORKLOG-VIEWr9   r8   r;   acl_owned_projectsc                 S   s   h | ]
}|j qS r   r   )rT   Zprojectr   r   r   rV   >  s   z-CmfAccessList.check_access.<locals>.<setcomp>--r   rO   Zcmf_owner_idrB   Zcmf_owner_assistantsINrN   rF   
request_iddebugz2CmfAccessList.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=(rQ   )NN)1rr   __dict__r   r   r}   Zload_fieldsr   valuer   r2   r=   subject_full_group_listanonymous_userZsharelink_anonymous_userCmfPersonGroupsharelink_grouprl   r   r   hasattr
startswith	api_scoper]   r   Zcheck_project_role_accesscheck_accessr|   r   r   r   Zis_not_nullZ%project_perm_timetrackerhistory_cacheZCmfSecurityLevelZcheck_perm_security_levelr{   Z
CmfProjectrZ   r   rs   argsREDIS_DBredis_new_acl_keytimesleeprW   current_personr   CmfPermissionError)ra   re   r   rf   r   r   rg   rh   ri   rj   rk   r   r   r   r~   Z_CmfAccessList__gr   Zstat_keyresultZrequested_object_fieldr   r   _userr   Zppp_cache_keyZppp_resZsec_resZ_acl_owned_projectseacl_idr   r   )r   r   r~   r   ra   r   r   re   r   rk   ri   rf   rg   rh   r   r   r   r   r      s<   )p&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_idrB   Zparent_modelr   c                 S   s   h | ]}|j rt|j qS r   )rA   rR   rS   )rT   Zrelationr   r   r   rV   p  s   z8CmfAccessList.subject_full_group_list.<locals>.<setcomp>rA   rP   c                    s"   h | ]}t j| d  rqS )rM   )cmfutilZget_obj_by_id)rT   Zgroup_idrN   r   r   r   rV   x  s      )
r   r   r2   rG   r   ZRelationCacherZ   rl   rR   rS   )rU   
subject_idr   rN   _kwargsZgroup_model_namesZrelation_filterr   r   r   r   r   i  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)rU   r   r   rA   r   r   r   r   c                 S   s   g | ]
}|j qS r   )rA   )rT   rp   r   r   r   
<listcomp>  s     z2CmfAccessList.subject_list_acl.<locals>.<listcomp>)rN   rF   order_by)r   r2   rG   rZ   r%   )	ra   rU   r   rN   rF   r   r   Z	member_ofZrelated_rulesr   r   r   r@   {  s    zCmfAccessList.subject_list_aclcheck_admin_modec                 C   s    t jst jrdS |rt| dS )NTF)rr   r   r   r   )messager   r   r   r   r     s
    zCmfAccessList.check_admin_modec                   C   sB   t j jtjjjddkr2tjs2tj	js2t
ddt_dt_d S )NTr   activate_admin_mode)r2   r   Zadmin_groupr   rr   r   Zrg_member_ofZall_parentsZ
is_supportr   r   r   r   r   r   r   r   r     s    z!CmfAccessList.activate_admin_modec                 C   s   dt _dt _dt _dS )u   Первоначальная инициализация контекста, чтобы работали g.current_person, в т.ч. создание пользователей.TN)rr   r   r   r   )ra   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_nftjtjkt_t	j
jtjddt_tjjstjtjkrtjdkr| jstt	j j| _tj| j tjrdt_tjrtjrt	j }tj|jj dS )u\   Сейчас уже должны быть рабочие g.current_person и g.system_personr   NTr   Zsd_api)r{   ZAPPrr   r   Zsystem_personr   r0   r   r   r2   r=   r   r   r   r   _CmfAccessList__guest_group_idr   r   Zguest_groupr   rl   Zsharelink_access_requestZsharelink_access_grantedr   r   )ra   r   r   r   r   setup_context  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   rl   r   trigger_reload)ra   Zsubjects_idsr   Zaffected_subjects_idsr   rb   r'   rp   r   r   r   subject_changed_hook  s    	z"CmfAccessList.subject_changed_hookr   u,   Применение изменений ACL 
   )Z	only_onceZonly_once_argsdescriptionZshow_bg_progressbarZ	countdownc           	         s  t d tj  d}t  | s(d g}  fdd| D ]X}|d krXd}t d  qtjj|}|slq:|j	dkrd}t d  q|g q:|st d	|  d
   t
tdd }d}dt| }|D ]}||7 }|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rtt d|j  t|jd  qt|j| qt d d S )Nzacl::proccess_chanded_acl startFc                    s<   | sd S | D ]*}| krq  | tjj| qd S r(   )rl   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_job.<locals>.process_acl_listTz8process_changed_acl(): acl_id is None, drop_all_cache!!!ry   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*   z8CmfAccessList.proccess_chanded_acl_job.<locals>.<lambda>r   r5   c                 S   s   h | ]
}|j qS r   r   )rT   r   r   r   r   rV     s   z9CmfAccessList.proccess_chanded_acl_job.<locals>.<setcomp>r   r   perm_effective_acl_idr   e   )rN   rF   slicez3acl::proccess_chanded_acl run invalidate_ids model=z len=z>acl::proccess_chanded_acl too many, flush all cache for model=zacl::proccess_chanded_acl end)rr   r   r2   r=   rd   r0   r?   r'   r]   r   r%   r   Ziter_modelsr`   Z	CMF_CACHEZinvalidate_idsr_   rZ   )	r   Zdrop_all_cacher   r'   Z
model_listZpctZpct_steprc   Zobj_idsr   r   r   proccess_chanded_acl_job  sT    

	


 z&CmfAccessList.proccess_chanded_acl_jobCmfAccessList:changed)Zchannelc                 K   s"   dd }t d dat| d S )Nc                	   S   sP   t d td trDt d d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 reloadFzacl::reload handler skip)
rW   r   r   _acl_need_reloadr1   ZappZcmf_contextr2   r=   rd   r   r   r   r   handler0  s    
z,CmfAccessList.on_acl_change.<locals>.handlerzacl::reload spawn handlerT)rW   r   geventZspawn)datar   r   r   r   r   on_acl_change+  s    zCmfAccessList.on_acl_changec                 C   s   t d g }|d k	rDtdddd |D i |D ]}|t| q0|rftddt|i || ntdd  d }t| jd|id d S )Nzacl::reload triggerr   r   c                 S   s   g | ]}t |qS r   )r   )rT   access_list_idr   r   r   r   J  s     z0CmfAccessList.trigger_reload.<locals>.<listcomp>r   )kwargs)rW   Zcmf_emit_server_eventr[   r   Zschedule_deferred_jobr   )ra   r   r   Zjob_access_list_idr   r   r   r   @  s    
zCmfAccessList.trigger_reloadc                    s   t   dddg S )Nr   Zpolicyparent)supersave_preload_fieldsrI   	__class__r   r   r   V  s    z!CmfAccessList.save_preload_fieldsc                 C   s
   d|  S )NzCmfAccessList::created:r   )r   r   r   r   r   Y  s    zCmfAccessList._new_acl_keyc                    s<   |  | j | jr.tjj| | jtjdd t	 j
f |S )Nr   )ex)r   r   rk   r   r   r0   r   rr   r   r   save)rI   r   r   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_idrO   rB   r   r   )r1   r2   r   r^   extendrZ   r   )rI   r   r   rc   r   r   r   find_acl_usagee  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   rH   )rI   r   usager   r   r   rH   t  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   )rI   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   rG   r   _acl_spread_inheritance)rI   r   r   r   r   r     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   )rI   Z_ruler   r   r   save_rule_hook  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   rB   r   z!=rE   Tr   )	r   r   r2   rG   rZ   r   r   r=   r   )rI   rp   r   r   r   delete_rule_hook  s    zCmfAccessList.delete_rule_hook)NNNNNNNNNNTNNN)NNFN)NNNNN)r   T)N)N)NN)N)4r   r   r   __doc__ZTEXKOM_no_cacher?   r&   r   r   r1   rN   cmf_access_listr=   Zapi_methodsrJ   classmethodrd   r,   r   r   r2   r3   r/   r"   r   staticmethodr   r@   r   r   r   r   r   r   Zcmf_deferred_jobr   Zon_server_eventr   r   r   r   r   r   rH   r   r   r   r   r   r  r  __classcell__r   r   r   r   r=   B   s   


C
              
   Q	


!
   L

r=   )%r#   ru   rR   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   Z_acl_changed_idsr4   r   Z_policy_prioritiesr|   r   	Exceptionr<   rN   r  r=   r   r   r   r   <module>   s4     
