U
    Įwh	                     @   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 d dlmZ d dlmZ d dlmZ d dlmZ d d	lT d
dlmZ 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)AES)SHA256)RSA)get_random_bytes)
PKCS1_v1_5)pad)*   )
send_email   )auth)LdapStatusCode)
Supervisorc                	       s,  e Zd ZdZdZejeej	
ejdddgdZedd Zed	d
 Zdd Zedd ZddddZedd ZdeddZdd ZdfddZeedddZdge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$d0d1 Z%d2d3 Z&ed4d5 Z'ed6d7 Z(ed8d9 Z)d:d; Z*dhd<d=Z+did>d?Z,ed@dA Z-edBdC Z.djdEdFZ/dke0e1e1e1edGdHdIZ2edJdK Z3edldLdMZ4 fdNdOZ5dPdQ Z6edRdS Z7ee8dTdUdVZ9edWdX Z:edYdZ Z;ed[d\ Z<edmd]d^Z=ee>dd_dd`dadbdc Z?  Z@S )nCmfAuth    
   modulesr   Z	templates)loaderc                 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_resp1   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<   r6   T)absolutezservicedesk/auth/?zauth/N)reset_pass_set_datadictreset_password_hashr6   auth_base_hrefurllibparse	urlencode)r   ZendpointZsdeskparamsr   r   r   reset_pass_linkN   s    
"zCmfAuth.reset_pass_linkc              	   C   s4  t jj|dgd}|sPt $ t jjddd |ddddd	 W 5 Q R X td
|jr|jt		 kr|jd t		 d krt $ t jjddd |ddddd	 W 5 Q R X t
ddtj }| }| jd}|j|td}t j }|j|jj||d t " t jjddd d|iddd	 W 5 Q R X d S )Nreset_password_expiresr6   fieldsZrestore_password_get_linkr   zLogin not foundr6   reasonfailT)operatecmf_model_nameparent
audit_dataresult_statuscurrent_transactionu,   Такого пользователя нетQ i,  zAlready sentu   Вам на почту уже отправлена ссылка для сброса пароля. Для повторной отправки повторите попытку позже.uC   Ссылка для восстановления доступа к zreset_password_email.html)Zrestore_linkconfig)subjectr6   ok)modelsr   getcmfutildisable_aclCmfAuditaudit_eventZCmfAuthErrorrJ   r)   ZCmfErrorrW   HOSTNAME_FQDNrI   
_jinja_envZget_templateZrenderZCmfPluginMailBoxZget_local_mailboxZsend_messageemailvalue)r5   r6   r   rX   linktemplatemessageZmail_boxr   r   r   send_pass_link]   sH    

  

  


  zCmfAuth.send_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   rV   )r$   Ztoken_urlsaferC   r(   r)   rJ   save)r   r=   r   r   r   rA   ~   s    
zCmfAuth.reset_pass_set_datac                 C   s   | j  pt | j kS )u_    Проверяем что ссылка для сброса пароля не протухла )rJ   r)   r   r   r   r   reset_pass_is_expired   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typrV   )r6   expscope.)r6   rc   r(   r)   rm   r   	b64encodejsondumpsr3   decode)r   dayspayloadheaderr   r   r   
create_jwt   s    
zCmfAuth.create_jwt)jwtc                 C   sF   t t| d   }t t| d   }| d| S )Nru   rt   rn   )r   ro   rp   rq   r3   rr   )rw   ru   rt   r   r   r   
jwt_to_str   s    zCmfAuth.jwt_to_strc                 C   s^   |d kr|  |}ttj}t }||  ||}t	
| }| d| }|S )Nrn   )rv   r   r4   APPZrsa_private_keyr   updater3   signr   ro   rr   )r   rs   rw   Zsignerdigestr{   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 )Nrn   rl   u9   Время жизни токена закончилосьru   rt   )r   r   r4   rz   r3   r   r   r   ry   rsa_public_keyverifyrr   rp   loadsr(   r)   gdebug)rjwtru   rt   	signaturer|   rw   verifierZverifiedr   r   r   rsa_verify_unpack_jwt   s"    


zCmfAuth.rsa_verify_unpack_jwtc                 C   sJ   |  d\}}}t| }t| }t|}t|}||dS )Nrn   r   )r   r   r   rr   rp   r   )r   ru   rt   r   r   r   r   rsa_unpack_jwt   s    

zCmfAuth.rsa_unpack_jwtc              
   C   s   |  d\}}}t }| d| }||  t|}d}z$t|}t	|}	|	
||}W n@ tk
r }
 z"d|
 }t| t||
W 5 d }
~
X Y nX |S )Nrn   Fu,   Не удалось разобрать JWT: )r   r   r4   rz   r3   r   r   r   Z
import_keyr   r   
ValueErrorloggingerror)r   Zrsa_public_key_bytesru   rt   r   r|   rw   r}   r   r   er   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)emptyrt   r6   rm   z/custom/org_name  :r	   uC   from_jwt: Доступ по билету тех поддержки zfrom_jwt: jwt is ok, z, z, is_local=)r   r   r   	Exceptionry   logger	exceptionr6   rb   rm   Zjwt_is_supportZjwt_is_match_orgospathexistsPROJECT_DIRopenreadstripr   
startswithglobal_settingsZsupport_mode)r5   r   rw   r   objorg_namefZpermr   r   r   from_jwt   s@    




"zCmfAuth.from_jwtc           	      C   s4  | 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rl|jd\}}tjj|drltjj|dgdD ]}z||jj|}W n: tk
r } zt	
d| d|  W 5 d }~X Y nX |tjkr q$nt|tjk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***)filterrL   r6   rb   Zident_failedr   rO   Tr
   	rP   rQ   rR   rS   rT   rU   security_levelparent_nameparent_codeus   Превышено количество попыток ввода пароля для учетной записи "u$   ", вход заблокированauth_failedzToo many failed login attemptsrM   un   Превышено количество попыток ввода пароля, повторите через u    минутZ
allow_ldap@)domainplugin.*)r   rL   u/   Ошибка авторизации через z: auth_successedZldap)r6   typerY   Z
allow_basebase) r[   cmfutilr\   r]   rZ   r^   r_   fail_block_end_dater   r   r6   	cmf_alertr   auth_fail_timeoutauth_optionsrc   r   CmfAuthLdapPlugincountlistZsigninr   r   r   ZINVALID_CREDENTIALSSUCCESS_is_permanent_blockauth_success_hookr   check_secretauth_fail_hook)	r5   r6   Zchallenge_respr   _r   auth_pluginZldap_status_coder   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 blockrM   rO   Tr
   r   ul   Учетная запись заблокирована, обратитесь к администраторуF)r   r   auth_fail_permanent_blockfail_permanent_blockr   r6   r   r   r\   r]   rZ   r^   r_   r   r   r   r   r   r   h  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:r      kr   r   zRequire captcharM   rO   Tr
   r   i  zBad captcha)r   r   Zauth_check_captchafail_try_counterrequestvaluesr[   ry   ZREDIS_DBjoinrandomchoicesstringdigitssetrZ   r^   r_   abortrr   )r   r6   r   Zdb_keyZ
db_captchaZnew_captchar   r   r   _auth_check_captcha  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   r   r   Zauth_fail_try_countr   r   datetimenow	timedeltar   rc   r   ri   r   r   r   r   r     s    
zCmfAuth.auth_fail_hookc                 C   s   d| _ |   d S )Nr   )r   ri   r   r   r   r   r     s    zCmfAuth.auth_success_hookc                 C   s"   |  }||_ ||_||| |S r#   )r6   rb   set_pass_hash)r5   r6   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   r3   r(   NotImplementedError)
r5   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   r6   rm   rK   r   r   rY   Tr
   )	rP   rQ   rS   rR   rT   rU   r   r   r   r   rO   )r[   r   r   rc   r   r   r\   r]   rZ   r^   r_   )r5   r6   r7   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)rR   Zorder_byslicerL   )
r   r   Zpassword_check_historyrZ   CmfAuthHistoryZslistr   r   ZCmfAuthReusePasswordErrorr   )r   r7   historyr   r   r   check_history  s       
zCmfAuth.check_historyc                 C   s(   t |}t |}| j|||d d S )Nr7   )r0   r1   set_pass_hash_bytes)r   r   r"   r7   
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   r6   FT)Z	only_data)r   r   ro   rr   r   Zpassword_changed_dateZset_nowrZ   	CmfPersonr[   r6   Zpassword_must_changer\   r]   ri   )r   r   r   r7   r   r   personr   r   r   r     s    



zCmfAuth.set_pass_hash_bytesc                 C   s   t jS r#   )r   r   )r5   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   r   zutf-8)	rangeappendr   choicer   ascii_lettersr   r   r3   )r5   charsir   r   r   gen_salt  s    zCmfAuth.gen_salt   c           	      C   s   |  dg |sPttjjj|}tjjj}tjjj}tjjj}| j	||||d}t
|  }td| |d}| j|||o~|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:
        r6   )lengthis_upper_symbol	is_numberis_special_symbolr-   r.   r   reset_passwordr   NrY   Tr
   )	rP   rQ   rS   rR   rT   rU   r   r   r   )Zload_fieldsmaxr   r   Zpassword_min_lengthrc   Zpassword_min_upper_symbolZpassword_min_numbersZpassword_min_special_symbol_generate_passwordr   r   r2   r   r3   r   r   r   r\   r]   rZ   r^   r_   r6   )	r   r   r7   forcer   r   r   r"   r   r   r   r   r     s4    


  
    zCmfAuth.reset_password)r   r   r   r   returnc                 C   s   g }t jt j t j }|r6|t j7 }|tt j |rL|tt j |rb|tt j tj||t	| d}|
| t| d|S )Nr   r   )r   r   r   Zascii_uppercaseZpunctuationr   r   r   r   r/   extendZshuffler   )r   r   r   r   r   Zpassword_dataZlettersZextra_symbolsr   r   r   r   #  s    


zCmfAuth._generate_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   rb   rY   Tr
   )	rP   rQ   rR   rS   rT   rU   r   r   r   )
rW   r`   r   r   r   r\   r]   rZ   r^   r_   )r5   r7   rb   rX   Zmsg_contentsr   r   r   r   8  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)r6   rg_member_ofT)r?   Zinternalzauth/signup)r6   Zgen_pwd)data   u>   Не удалось создать учётную записьaccess_token)r  )Zrequestsr   rZ   r   ri   ZpostrD   Zstatus_coder   rB   Zcookiesr[   )r5   r6   r   r   sr   rr   r   r   create_personC  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 )Nr   r   r   )rR   r   )groups
is_changedrm   is_nullrc   r   superri   r   r\   r]   rZ   r   )r   argskwargsr   Zgrp_namer}   	__class__r   r   ri   [  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 )r   r   )r   ).0r   r   r   r   
<listcomp>n  s     z)CmfAuth.prepare_scope.<locals>.<listcomp>r   )rm   r  r   r   r   r   r   r   prepare_scopej  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Falsert   issuJ   Не удалось прочитать публичный ключ EvaTeamuE   Не удалось валидировать токен от EVA_APP r   !)
r   rW   ZEVA_ACCOUNT_USEr   Zread_crm_pub_keyRuntimeErrorr   r   r   r   )r5   eva_app_tokenrw   r  Zcrm_pub_keyr   r   r   r   r   check_tokenp  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
        rt   r   r6   r  rm   r   reg_org_name_listrb   r   NZ	old_loginrK   r   u(   Создали пользователя r   Zcmf_deletedu4   Пользователь {} удален из Eva {}Tr   u,   Добавляем пользователю u    права u    в Eva r   z
user_dict=zuser.groups=zuser.scope=u)   Не удалось обработать resultrY   )r  r'   lowerr   r[   r6   rZ   r   r   r   ri   rb   r   r  r  r  r   formatremover  r   Zcommit_with_eventrm   r   r   )r5   r  r  r
  r  rw   r   Z	user_dictr6   _fieldsuserrb   r   Zgrp_coder   r   r   rpc_account_sync_push  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_frt   r   pluginr   r	   )ext_idrL   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  rY   )rW   ZIS_BOX_VERSIONr  rZ   Z	CmfPluginr[   r'  ri   r   r   r&  r   delete)r5   r  Zauth_pluginsr
  r  r%  rw   r   r   r&  Zauth_plugin_objr   r   r   rpc_account_plugin_push  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rs   ZAUTH_SESSION_COOKIE_DOMAINZsession_tokenTLaxr   expiresZsecureZhttponlyZsamesite)Zreauth_dater   r   Zaccess_token_expires_inrc   rW   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,  )r1  r~   rW   TOKEN_TTL_DAYSr   r   r   r.  )r   r3  r5  r4  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,  )r9  r:  )r   r   r   rW   r7  r1  )r3  r5  r-  r4  r   r   r   r2    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   )rL   r   r   )r   r   r   r   r   rZ   r   r   r   ri   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)FFF)N)N)A__name__
__module____qualname__r&   Z	ts_lengthZjinja2ZEnvironmentZFileSystemLoaderr   r   r   rW   r   ra   propertyr    r"   r,   classmethodr;   rI   rg   rA   rj   rv   staticmethodrB   rx   r'   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r(   boolr   r   r  ri   r  r  r   r  r)  r6  r8  r2  Zcmf_deferred_jobr>  __classcell__r   r   r  r   r      s   




 






'
Z








      


B
!

r   ) r   r   r2   rp   r   r$   r   r)   rE   r   ZCrypto.Cipherr   ZCrypto.Hashr   ZCrypto.PublicKeyr   ZCrypto.Randomr   ZCrypto.Signaturer   ZCrypto.Util.Paddingr   Zcmf.includerb   r   rL   r   Zenumsr   Z
supervisorr   r   r   r   r   r   <module>   s*   