U
    j*gP                     @   s   d dl Z d dlZd dlZd dlZd dlZd dl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mZ d dlmZmZ d dlmZ d dlmZ d dlmZ d d	lmZmZ d d
lT ddlmZ ddlmZ ddl m!Z! G dd dej"Z"dS )    N)pbkdf2_hmac)	urlencode)AES
PKCS1_OAEP)SHA1SHA256)RSA)get_random_bytes)
PKCS1_v1_5)padunpad)*   )
send_email   )auth)
Supervisorc                       s  e Zd ZdZdZedd Zedd Zdd Ze	d	d
 Z
d[ddZd\ddZdd Zd]ddZeedddZd^edddZedd Zedd Zed d! Ze	d"d# Ze	d$d% Zd&d' Zd(d) Zd*d+ Zd,d- Ze	d.d/ Ze	d0d1 Ze	d2d3 Zd4d5 Z d_d6d7Z!d`d8d9Z"e	d:d; Z#e	d<d= Z$dad?d@Z%e	dAdB Z&e	dbdCdDZ' fdEdFZ(dGdH Z)e	dIdJ Z*e	e+dKdLdMZ,e	dNdO Z-edPdQ Z.edRdS Z/edcdTdUZ0ee1ddVddWdXdYdZ Z2  Z3S )dCmfAuth    
   c                 C   s   t | jdd S )N$base64	b64decode	pass_hashsplitself r   ./modules/auth/models/auth.pykey!   s    zCmfAuth.keyc                 C   s   t | jdd S )Nr   r   r   r   r   r    salt%   s    zCmfAuth.saltc                 C   s2   t | j}|ttt 7 }|| j 7 }|S N)secrets	token_hextoken_lengthstrinttimer#   hex)r   server_challenger   r   r    gen_server_challenge)   s    zCmfAuth.gen_server_challengec                 C   s   d| j d  }}||| }||d  }}||| }|t| }}t||| }td| |d}	t| j }
t	t
t }||
 | | }ttj}t|	tj|}|t| tj}||  S )Nr   r   r   sha256順 )r'   lenbytesfromhexhashlibr   encoder%   r&   r(   r)   r*   r	   r   Z
block_sizenewZMODE_CBCZencryptr   r+   )clsloginpasswordr,   startendZserver_randomZserver_timestampZserver_saltZtest_keyZclient_randomsecretZivZcipherZencrypted_secretr   r   r    test_gen_server_challenge_resp/   s&       
z&CmfAuth.test_gen_server_challenge_resprestore_passwordFc                 C   sv   | j dd t| jd}|dkr*| j|d< |rPtdd d| d	tj| S tdd d
| d	tj| S dS )u   
        Сформируем ссылку на сброс и отправим её пользователю.
        Альтернативно переиспользуется для приглашения нового пользователя.
        F)reset)hr=   r7   T)absolutezservicedesk/auth/?zauth/N)reset_pass_set_datadictreset_password_hashr7   auth_base_hrefurllibparser   )r   ZendpointZsdeskparamsr   r   r    reset_pass_linkL   s    
"zCmfAuth.reset_pass_linkTc                 C   s:   |rdnt d| _|rdntt d | _|   dS )u  
        Выставляем (или сбрасываем) секретик для сброса пароля
        Не очень хорошо, наверное хранить его в чистом виде, если упрут базу account будет печально,
        т.к. по сути даст доступ ко всем, кто в течение часа до угона БД запросил восстановление пароля.
        По хорошему хэшировать чем-то, что хранится отдельно от БД и периодически менять.
        Но это TODO-преTODO, т.к. паранойя.
        12 байт рандома дают нам 2**(8*12)=79,2E+27 значений. Перебирать по 1 в сек по сети - 2,5E+21 лет.
        :param reset - если True, выставляем всё в None, если False - генерируем новые значения
        N   Q )r%   Ztoken_urlsaferD   r)   r*   reset_password_expiressave)r   r>   r   r   r    rB   [   s    
zCmfAuth.reset_pass_set_datac                 C   s   | j  pt | j kS )u_    Проверяем что ссылка для сброса пароля не протухла )rL   r*   r   r   r   r    reset_pass_is_expiredi   s    zCmfAuth.reset_pass_is_expiredNc                 C   st   ddd}|d kr6| j jtt d|  | jjd}tt|	 
 }tt|	 
 }| d| S )NZRS256ZJWT)ZalgtyprK   )r7   expscope.)r7   valuer)   r*   rQ   r   	b64encodejsondumpsr4   decode)r   dayspayloadheaderr   r   r    
create_jwtm   s    
zCmfAuth.create_jwt)jwtc                 C   sF   t t| d   }t t| d   }| d| S )NrZ   rY   rR   )r   rT   rU   rV   r4   rW   )r\   rZ   rY   r   r   r    
jwt_to_stry   s    zCmfAuth.jwt_to_strc                 C   s^   |d kr|  |}ttj}t }||  ||}t	
| }| d| }|S NrR   )r[   r
   r5   APPZrsa_private_keyr   updater4   signr   rT   rW   )r   rX   r\   Zsignerdigestra   resr   r   r    rsa_sign_pack_jwt   s    

zCmfAuth.rsa_sign_pack_jwtc                 C   s   |  d\}}}t }| d| }||  t|}ttj	}|
||}|s^d S t| }t| }t|}t|}tt t|d krtdt |d  d S ||dS )NrR   rP   u9   Время жизни токена закончилосьrZ   rY   )r   r   r5   r`   r4   r   r   r
   r_   rsa_public_keyverifyrW   rU   loadsr)   r*   gdebug)rjwtrZ   rY   	signaturerb   r\   verifierZverifiedr   r   r    rsa_verify_unpack_jwt   s"    


zCmfAuth.rsa_verify_unpack_jwtc                 C   sJ   |  d\}}}t| }t| }t|}t|}||dS )NrR   re   )r   r   r   rW   rU   rh   )rk   rZ   rY   rl   r   r   r    rsa_unpack_jwt   s    

zCmfAuth.rsa_unpack_jwtc           	      C   s^   |  d\}}}t }| d| }||  t|}t|}t	|}|
||S r^   )r   r   r5   r`   r4   r   r   r   Z
import_keyr
   rg   )	rk   Zrsa_public_key_bytesrZ   rY   rl   rb   r\   rf   rm   r   r   r    rsa_verify_jwt   s    


zCmfAuth.rsa_verify_jwtc              
   C   s|  t d |st d d S z| |}W n2 tk
r\ } ztjd d }W 5 d }~X Y nX |spt d d S | dd}|d d |_|d d |_|d d	 |_	d |_
d |_d }tjtd
 rttd
 }|  }W 5 Q R X | |_|j	pddD ]T}|r|| dr"d|_|dkr t jjr t d|  d|_
d|_q t d|j d|j	 d|j  |S )Nzfrom_jwt: startzfrom_jwt: warn not jwtzfail unpack jwtz2from_jwt: warn not cls.rsa_verify_unpack_jwt(rjwt)T)emptyrY   r7   rQ   z/custom/org_name  :r   uC   from_jwt: Доступ по билету тех поддержки zfrom_jwt: jwt is ok, z, z, is_local=)ri   rj   rn   	Exceptionr_   logger	exceptionr7   emailrQ   Zjwt_is_supportZjwt_is_match_orgospathexistsZPROJECT_DIRopenreadstripr   
startswithglobal_settingsZsupport_mode)r6   rk   r\   eobjorg_namefZpermr   r   r    from_jwt   s@    




"zCmfAuth.from_jwtc           	      C   s  | j dd|gdgd}|s0| j dd|gdgd}|sJ| j dd|gdgd}|stjj ( tjjddd d|id	d
d||d	 W 5 Q R X d S |jrt	
d|j d tjj 0 tjjddd |jddd	d
d|j|jd	 W 5 Q R X tdt	jj d d S |jj drR|jd\}}tjj|drRtjj|dgdD ]}z||jj|}W n: tk
r } zt	
d| d|  W 5 d }~X Y npX |rD| r d S |  tjj 6 tjjddd |jjdddd
d|jj|jjd	 W 5 Q R X |  S qDtjj 6 tjjddd |jjddd	d
d|jj|jjd	 W 5 Q R X |jr|jj dr| ||jjr| rd S |  tjj * tjjddd |d ddd
d||d	 W 5 Q R X |S tjj * tjjddd |d dd	d
d||d	 W 5 Q R X |  d S )!N	ext_loginZILIKEz***)filterfieldsr7   rx   Zident_failedr   failTr   	operatecmf_model_nameparent
audit_dataresult_statuscurrent_transactionsecurity_levelparent_nameparent_codeus   Превышено количество попыток ввода пароля для учетной записи "u$   ", вход заблокированauth_failedzToo many failed login attemptsr7   reasonun   Превышено количество попыток ввода пароля, повторите через u    минутZ
allow_ldap@)domainplugin.*)r   r   u/   Ошибка авторизации через z: auth_successedZldap)r7   typeokZ
allow_basebase)getcmfutilcmfutildisable_aclmodelsCmfAuditaudit_eventfail_block_end_dateri   rj   r7   	cmf_alertr   auth_fail_timeoutauth_optionsrS   r   CmfAuthLdapPlugincountlistZsigninr   ru   _is_permanent_blockauth_success_hookr   check_secretauth_fail_hook)	r6   r7   Zchallenge_respr   _r   auth_pluginZis_successful_signinr   r   r   r    get_by_challenge_resp   s    
    
(

    
    

    
    zCmfAuth.get_by_challenge_respc                 C   st   t jjrp| jrpt d| j d tjj	 0 t
jjddd | jddddd	| j| jd
	 W 5 Q R X td dS dS )Nu   Учетная запись "u3   " перманентно заблокированаr   r   zPermanent blockr   r   Tr   r   ul   Учетная запись заблокирована, обратитесь к администраторуF)ri   r   auth_fail_permanent_blockfail_permanent_blockrj   r7   r   r   r   r   r   r   r   r   r   r   r   r    r   <  s"    
zCmfAuth._is_permanent_blockc                 C   s   t jjsd S | jdkrd S tjdd }d| }tj|}d	t
jtjdd}tj|| |stjjddd |d	d
ddd||d	 td | }|r||krtjjddd |dd
ddd||d	 td d S )Nr   captchazauth:user_login_captcha:rr      )kr   r   zRequire captchar   r   Tr   r   i  zBad captcha)ri   r   Zauth_check_captchafail_try_counterrequestvaluesr   r_   ZREDIS_DBjoinrandomchoicesstringdigitssetr   r   r   abortrW   )r   r7   r   Zdb_keyZ
db_captchaZnew_captchar   r   r    _auth_check_captchaS  s@    


    
    zCmfAuth._auth_check_captchac                 C   s\   |  j d7  _ | j tjjkrPtjjr,d| _ntj tjtjj	j
d | _d| _ |   d S )Nr   T)Zminutesr   )r   ri   r   Zauth_fail_try_countr   r   datetimenow	timedeltar   rS   r   rM   r   r   r   r    r   r  s    
zCmfAuth.auth_fail_hookc                 C   s   d| _ |   d S )Nr   )r   rM   r   r   r   r    r     s    zCmfAuth.auth_success_hookc                 C   s"   |  }||_ ||_||| |S r$   )r7   rx   set_pass_hash)r6   r7   hashr#   r   r   r   r    new_from_login_hash_salt  s
    z CmfAuth.new_from_login_hash_saltc           
      C   sl   | d}|d }|dkrZ|dd  \}}}t|}t|}	|	td| |t|kS td| d S )Nr   r   Zpbkdf2_sha256r   r.   zNot Implemented for )r   r   r   r   r4   r)   NotImplementedError)
r6   r;   Zsecret_hashZ
hash_partsZ	hash_algoZn_itersalt_b64hash_b64Zsalt_bZhash_br   r   r    r     s    


zCmfAuth.check_secretc                 C   s   | j |dddgd}|rf| ||jjrftjj ( tj	j
ddd|id ddd	||d
	 W 5 Q R X |S tjj ( tj	j
ddd|id ddd	||d
	 W 5 Q R X d S )Nr   r7   rQ   r7   r   r   r   r   Tr   )	r   r   r   r   r   r   r   r   r   r   r   )r   r   r   rS   r   r   r   r   r   r   r   )r6   r7   r8   r   r   r   r    from_login_password  s2    
    
    zCmfAuth.from_login_passwordc                 C   sR   t jjsd S tjj| dgddgddgdD ]"}|jr*| ||jr*t|j	q*d S )Nz-cmf_created_atr      r   cmf_created_at)r   Zorder_byslicer   )
ri   r   Zpassword_check_historyr   CmfAuthHistoryZslistr   r   ZCmfAuthReusePasswordErrorr   )r   r8   historyr   r   r    check_history  s       
zCmfAuth.check_historyc                 C   s(   t |}t |}| j|||d d S )Nr8   )r1   r2   set_pass_hash_bytes)r   r   r#   r8   
hash_bytes
salt_bytesr   r   r    r     s    

zCmfAuth.set_pass_hashc              	   C   s   |d k	r|  | t| }t| }d| d| | _| j  tjj	| j
d}|r|j  d|_t  |jdd W 5 Q R X d S )Nzpbkdf2_sha256$100000$r   r7   FT)Z	only_data)r   r   rT   rW   r   Zpassword_changed_dateZset_nowr   	CmfPersonr   r7   Zpassword_must_changer   r   rM   )r   r   r   r8   r   r   personr   r   r    r     s    



zCmfAuth.set_pass_hash_bytesc                 C   s   t jS r$   )ri   r   )r6   r   r   r    current_auth  s    zCmfAuth.current_authc                 C   s:   g }t dD ]}|ttjtj  qd|dS )N   rr   zutf-8)	rangeappendr   choicer   ascii_lettersr   r   r4   )r6   charsir   r   r    gen_salt  s    zCmfAuth.gen_salt   c                    s   |  dg tjtj  |s8d fddt|D }t|  }t	d|
 |d}| j|||of|d tjj . tjjdd	d| jid
dd| j| jdd	 W 5 Q R X |S )uO   
        Генерирует новый пароль
        :return:
        r7   rr   c                 3   s   | ]}t  V  qd S r$   )r   r   .0r   Zlettersr   r    	<genexpr>  s     z)CmfAuth.reset_password.<locals>.<genexpr>r.   r/   r   reset_passwordr   Nr   Tr   )	r   r   r   r   r   r   r   r   r   )Zload_fieldsr   r   r   r   r   r   r   r3   r   r4   r   r   r   r   r   r   r   r   r7   )r   lengthr8   forcer#   r   r   r   r    r     s$    
    zCmfAuth.reset_passwordc                 C   s^   dt j }d|g}t||| tjj ( tjj	ddd d|idd||dd		 W 5 Q R X d S )
Nu&   Пароль для доступа к u   Пароль:send_passwordr   rx   r   Tr   )	r   r   r   r   r   r   r   r   r   )
configZHOSTNAME_FQDNr   r   r   r   r   r   r   r   )r6   r8   rx   ZsubjectZmsg_contentsr   r   r    r     s    
    zCmfAuth.send_passwordc                 C   sn   ddl m} | }tj||d}|  |jtddd d|ddd}|jd	kr\td
t	|j
ddS )Nr   )session)r7   rg_member_ofT)r@   Zinternalzauth/signup)r7   Zgen_pwd)data   u>   Не удалось создать учётную записьaccess_token)r   )Zrequestsr   r   r   rM   ZpostrE   Zstatus_coderu   rC   Zcookiesr   )r6   r7   r   r   sr   rr   r   r    create_person  s    
zCmfAuth.create_personc              	      s   | j jrZd| _| j jsZ| j D ].}| j | D ]}|  jd| d| 7  _q*q| jj | _t j||}| jjrt	
  tj| | jd  W 5 Q R X |S )Nrr   rs   rt   )r   r   )groups
is_changedrQ   is_nullrS   r~   superrM   r   r   r   r   r   )r   argskwargsr   Zgrp_namerc   	__class__r   r    rM     s    

zCmfAuth.savec                 C   s&   | j jrg S tdd | j dD S )Nc                 S   s   g | ]}| d d qS )rt   r   )r   r   r   r   r    
<listcomp>#  s     z)CmfAuth.prepare_scope.<locals>.<listcomp>rs   )rQ   r   r   r   r   r   r   r    prepare_scope  s    zCmfAuth.prepare_scopec                 C   s   |  |}tjdkr|S |d d }zt|}W n  tk
rR   d}t|Y nX | ||}|sd|d d  d}t	| t||S )NFalserY   issuJ   Не удалось прочитать публичный ключ EvaTeamuE   Не удалось валидировать токен от EVA_APP r   !)
ro   r   ZEVA_ACCOUNT_USEr   Zread_crm_pub_keyRuntimeErrorru   rp   loggingerror)r6   eva_app_tokenr\   r  Zcrm_pub_keyr  rg   r   r   r    check_token%  s    


zCmfAuth.check_token)usersc             
   O   sf  |  |}|d d }|D ]@}zt|d   }dddddd	g}	d
}
|dr| jt|d   |	d}
|
r||
_q| j||	d}
n| j||	d}
|
stj|d}
t	d|  |

  |dpd}t|  p||
_|d	pd}t|  pd
|
_|
jjr i |
_|
js.g |
_||
jkrF|
j| g |
j|< |d rt	d|| ||
jkr|
j| |
j|= d|
j_|

  W q|d D ]:}t	d| d| d|  |
j| | d|
j_q|d |
_t	d|  t	d|
j  |

  t  t	d|
j  W q tk
rZ   td|   Y qX qddiS )u   
        Метод вызывается из eva_app при синхронизации
        создает и привязывает пользователей
        https://bcrm.carbonsoft.ru/project/Document/DOC-003025#spec-0-ldap
        rY   r   r7   r   rQ   r   reg_org_name_listrx   r   NZ	old_loginr   r   u(   Создали пользователя rr   Zcmf_deletedu4   Пользователь {} удален из Eva {}Tr   u,   Добавляем пользователю u    права u    в Eva r   z
user_dict=zuser.groups=zuser.scope=u)   Не удалось обработать resultr   )r  r(   lowerr~   r   r7   r   r   r  rj   rM   rx   r   r   r   r
  r   formatremover   r   Zcommit_with_eventrQ   ru   rw   )r6   r  r	  r   r   r\   r   Z	user_dictr7   _fieldsuserrx   r   Zgrp_coder   r   r    rpc_account_sync_push7  sl    

  





zCmfAuth.rpc_account_sync_pushc                O   s  t js
d S dd }t jsd S | |}|d d }|D ]}tjj|d d dgd}	|	s`t }	||	|d  |d d |	_|	  tjj|d dd	gd}
|
st }
||
| ||
_	|d |
_|	|
_
|
  q6tjjd
ddd |D gdD ]}	|	  qddiS )Nc                 S   sF   |D ]<}|dks| ds|dr&qt| |rt| |||  qd S )N)idcodeZext_ipZcmf_Z_id)r   endswithhasattrsetattr)r   Zobj_dictZ
field_namer   r   r    copy_f~  s    
z/CmfAuth.rpc_account_plugin_push.<locals>.copy_frY   r   pluginr  r   )ext_idr   r   r  zNOT INc                 S   s   g | ]}|d  qS )r  r   )r   r   r   r   r    r     s     z3CmfAuth.rpc_account_plugin_push.<locals>.<listcomp>)r   r  r   )r   ZIS_BOX_VERSIONr  r   Z	CmfPluginr   r  rM   r   r   r  r   delete)r6   r  Zauth_pluginsr   r   r  r\   r   r   r  Zauth_plugin_objr   r   r    rpc_account_plugin_pushz  s2    



"
zCmfAuth.rpc_account_plugin_pushc                 K   sl   | j tj| jjd tjtjd }ttdtj	}|j
d|  f||dddd| tj|||f| d S )N)ZsecondsrX   ZAUTH_SESSION_COOKIE_DOMAINZsession_tokenTLaxr   expiresZsecureZhttponlyZsamesite)Zreauth_dater   r   Zaccess_token_expires_inrS   r   PROLONG_DAYSgetattrr   host
set_cookieZ	get_tokenr   set_nginx_token)r   responsecookie_kwargsr  cookie_domainr   r   r    set_session_token  s    $  	zCmfAuth.set_session_tokenc                 K   sL   |j d| jtjd df|tj tjtjtj d dddd| d S )Nr   r   r  Tr  r  )r#  rd   r   TOKEN_TTL_DAYSr   r   r   r   )r   r%  r'  r&  r   r   r    set_access_token  s    
  
zCmfAuth.set_access_tokenc                 K   s<   |st j  t jtjd }| jd||dddd| d S )Nr  nginx_auth_tokenpassword123Tr  r  )r+  r,  )r   r   r   r   r)  r#  )r%  r'  r  r&  r   r   r    r$    s        zCmfAuth.set_nginx_tokenu_   Разблокировка учетных записей по истечению времениz	@minutely)Z	only_oncedescriptionZ
system_jobZschedulec                  C   s^   t jjrd S tj } tjjdgdd| gddgd}|s:qZ|D ]}d |_|	  q>t
  qd S )Nr   <r   d   )r   r   r   )ri   r   r   r   r   r   r   r   r   rM   Z
cmf_commit)r   Zblocked_usersr   r   r   r    )cron_unblock_users_after_block_expiration  s    

z1CmfAuth.cron_unblock_users_after_block_expiration)r=   F)T)r   N)r   N)N)N)r   NN)N)N)4__name__
__module____qualname__r'   Z	ts_lengthpropertyr!   r#   r-   classmethodr<   rI   rB   rN   r[   staticmethodrC   r]   r(   rd   rn   ro   rp   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rM   r   r  r   r  r  r(  r*  r$  Zcmf_deferred_jobr0  __classcell__r   r   r   r    r      s   










'
X











B
!

r   )#r   r   r3   rU   r   r%   r   r*   rF   r   urllib.parser   ZCrypto.Cipherr   r   ZCrypto.Hashr   r   ZCrypto.PublicKeyr   ZCrypto.Randomr	   ZCrypto.Signaturer
   ZCrypto.Util.Paddingr   r   Zcmf.includerx   r   r   r   Z
supervisorr   r   r   r   r   r    <module>   s*   