B
    f                 @   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
dZd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	d/d0 Ze	d1d2 Zd3d4 Z d^d5d6Z!d_d7d8Z"e	d9d: Z#e	d;d< Z$d`d>d?Z%e	d@dA Z&e	dadBdCZ' fdDdEZ(dFdG Z)e	dHdI Z*e	e+dJdKdLZ,e	dMdN Z-edOdP Z.edQdR Z/edbdSdTZ0ee1ddUddVdWdXdY Z2  Z3S )c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   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   sha256i )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_passwordc             C   sL   | j dd t| jd}|dkr*| j|d< tdd d| d	tj| S )
u   
        Сформируем ссылку на сброс и отправим её пользователю.
        Альтернативно переиспользуется для приглашения нового пользователя.
        F)reset)hr9   r3   T)absolutezauth/?)reset_pass_set_datadictreset_password_hashr3   auth_base_hrefurllibparser   )r   Zendpoint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   iQ )r"   Ztoken_urlsafer@   r&   r'   reset_password_expiressave)r   r:   r   r   r   r>   W   s    
zCmfAuth.reset_pass_set_datac             C   s   | j  pt | j kS )u_    Проверяем что ссылка для сброса пароля не протухла )rG   r'   )r   r   r   r   reset_pass_is_expirede   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typiQ )r3   expscope.)r3   valuer&   r'   rL   r   	b64encodejsondumpsr0   decode)r   dayspayloadheaderr   r   r   
create_jwti   s    
zCmfAuth.create_jwt)jwtc             C   sF   t t| d   }t t| d   }| d| S )NrU   rT   rM   )r   rO   rP   rQ   r0   rR   )rW   rU   rT   r   r   r   
jwt_to_stru   s    zCmfAuth.jwt_to_strc             C   s^   |d kr|  |}ttj}t }||  ||}t	
| }| d| }|S )NrM   )rV   r
   r1   APPZrsa_private_keyr   updater0   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 )NrM   rK   u9   Время жизни токена закончилось)rU   rT   )r   r   r1   rZ   r0   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 )NrM   )rU   rT   )r   r   r   rR   rP   ra   )rd   rU   rT   re   r   r   r   rsa_unpack_jwt   s    

zCmfAuth.rsa_unpack_jwtc       	      C   s^   |  d\}}}t }| d| }||  t|}t|}t	|}|
||S )NrM   )r   r   r1   rZ   r0   r   r   r   Z
import_keyr
   r`   )	rd   Zrsa_public_key_bytesrU   rT   re   r\   rW   r_   rf   r   r   r   rsa_verify_jwt   s    


zCmfAuth.rsa_verify_jwtc          
   C   s  t d |st d d S y| |}W n2 tk
r\ } ztjd d }W d 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 d Q R X | |_xh|j	pddD ]T}|r|| dr$d|_|dkrt jjrt d|  d|_
d|_qW 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   r3   rL   z/custom/org_name  :r   uC   from_jwt: Доступ по билету тех поддержки zfrom_jwt: jwt is ok, z, z, is_local=)rb   rc   rg   	ExceptionrY   Zlogger	exceptionr3   emailrL   Zjwt_is_supportZjwt_is_match_orgospathexistsZPROJECT_DIRopenreadstripr   
startswithglobal_settingsZsupport_mode)r2   rd   rW   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 d 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 d Q R X tdt	jj d d S |jj drP|jd\}}tjj|drPxtjj|dgdD ]}y||jj|}W n: tk
r } zt	
d| d|  W d d }~X Y njX |rF| rd S |  tjj 6 tjjddd |jjdddd
d|jj|jjd	 W d Q R X |S qFW tjj 6 tjjddd |jjddd	d
d|jj|jjd	 W d 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 d Q R X |S tjj * tjjddd |d dd	d
d||d	 W d Q R X |  d S )!N	ext_loginZILIKEz***)filterfieldsr3   rp   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)r3   reasonun   Превышено количество попыток ввода пароля, повторите через u    минутZ
allow_ldap@)domainzplugin.*)r   r   u/   Ошибка авторизации через z: auth_successedZldap)r3   typeokZ
allow_basebase)getcmfutilcmfutildisable_aclmodelsCmfAuditaudit_eventfail_block_end_daterb   rc   r3   	cmf_alertrx   auth_fail_timeoutauth_optionsrN   r   CmfAuthLdapPlugincountlistZsigninr~   rn   _is_permanent_blockauth_success_hookr   check_secretauth_fail_hook)	r2   r3   Zchallenge_resprz   _r   auth_pluginZis_successful_signinry   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 d Q R X td dS dS )Nu   Учетная запись "u3   " перманентно заблокированаr   r   zPermanent block)r3   r   r   Tr   )	r   r   r   r   r   r   r   r   r   ul   Учетная запись заблокирована, обратитесь к администраторуF)rb   rx   auth_fail_permanent_blockfail_permanent_blockrc   r3   r   r   r   r   r   r   r   r   )r   r   r   r   r   8  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:rk      )kr   r   zRequire captcha)r3   r   r   Tr   )	r   r   r   r   r   r   r   r   r   i  zBad captcha)rb   rx   Zauth_check_captchafail_try_counterrequestvaluesr   rY   ZREDIS_DBjoinrandomchoicesstringdigitssetr   r   r   abortrR   )r   r3   r   Zdb_keyZ
db_captchaZnew_captchar   r   r   _auth_check_captchaO  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   rb   rx   Zauth_fail_try_countr   r   datetimenow	timedeltar   rN   r   rH   )r   r   r   r   r   n  s    zCmfAuth.auth_fail_hookc             C   s   d| _ |   d S )Nr   )r   rH   )r   r   r   r   r   |  s    zCmfAuth.auth_success_hookc             C   s"   |  }||_ ||_||| |S )N)r3   rp   set_pass_hash)r2   r3   hashr!   rz   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   r0   r&   NotImplementedError)
r2   r7   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 d Q R X |S tjj ( tj	j
ddd|id ddd	||d
	 W d Q R X d S )Nr   r3   rL   )r3   r   r   r   r   Tr   )	r   r   r   r   r   r   r   r   r   r   r   )r   r   r   rN   r   r   r   r   r   r   r   )r2   r3   r4   r   r   r   r   from_login_password  s    

zCmfAuth.from_login_passwordc             C   sV   t jjsd S xDtjj| dgddgddgdD ]"}|jr,| ||jr,t|j	q,W d S )Nz-cmf_created_atr      r   cmf_created_at)r   Zorder_byslicer   )
rb   rx   Zpassword_check_historyr   CmfAuthHistoryZslistr   r   ZCmfAuthReusePasswordErrorr   )r   r4   historyr   r   r   check_history  s    zCmfAuth.check_historyc             C   s(   t |}t |}| j|||d d S )N)r4   )r-   r.   set_pass_hash_bytes)r   r   r!   r4   
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 d Q R X d S )Nzpbkdf2_sha256$100000$r   )r3   FT)Z	only_data)r   r   rO   rR   r   Zpassword_changed_dateZset_nowr   	CmfPersonr   r3   Zpassword_must_changer   r   rH   )r   r   r   r4   r   r   personr   r   r   r     s    



zCmfAuth.set_pass_hash_bytesc             C   s   t jS )N)rb   r   )r2   r   r   r   current_auth  s    zCmfAuth.current_authc             C   s>   g }x(t dD ]}|ttjtj  qW d|dS )N   rk   zutf-8)	rangeappendr   choicer   ascii_lettersr   r   r0   )r2   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 d
Q R X |S )uO   
        Генерирует новый пароль
        :return:
        r3   rk   c             3   s   | ]}t  V  qd S )N)r   r   ).0r   )lettersr   r   	<genexpr>  s    z)CmfAuth.reset_password.<locals>.<genexpr>r+   i )r4   reset_passwordr   Nr   Tr   )	r   r   r   r   r   r   r   r   r   )Zload_fieldsr   r   r   r   r   r   r   r/   r   r0   r   r   r   r   r   r   r   r   r3   )r   lengthr4   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 d Q R X d S )
Nu&   Пароль для доступа к u   Пароль:send_passwordr   rp   r   Tr   )	r   r   r   r   r   r   r   r   r   )
configZHOSTNAME_FQDNr   r   r   r   r   r   r   r   )r2   r4   rp   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)r3   rg_member_ofT)r<   Zinternalzauth/signup)r3   Zgen_pwd)data   u>   Не удалось создать учётную записьaccess_token)r   )Zrequestsr   r   r   rH   ZpostrA   Zstatus_codern   r?   Zcookiesr   )r2   r3   r   r   sr   rr   r   r   create_person  s    
zCmfAuth.create_personc          	      s   | j jrbd| _| j jsbx<| j D ]2}x,| j | D ]}|  jd| d| 7  _q.W qW | jj | _t j||}| jjrt	
  tj| | jd  W d Q R X |S )Nrk   rl   rm   )r   r   )groups
is_changedrL   is_nullrN   rv   superrH   r   r   r   r   r   )r   argskwargsr{   Zgrp_namer]   )	__class__r   r   rH     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 )rm   r   )r   )r   r   r   r   r   
<listcomp>  s    z)CmfAuth.prepare_scope.<locals>.<listcomp>rl   )rL   r   r   r   )r   r   r   r   prepare_scope  s    zCmfAuth.prepare_scopec             C   s   |  |}tjdkr|S |d d }y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{   !)
rh   r   ZEVA_ACCOUNT_USEr   Zread_crm_pub_keyRuntimeErrorrn   ri   loggingerror)r2   eva_app_tokenrW   r   Zcrm_pub_keyr   r`   r   r   r   check_token!  s    


zCmfAuth.check_token)usersc         
   O   sn  |  |}|d d }xL|D ]B}y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s2g |
_||
jkrJ|
j| g |
j|< |d rt	d|| ||
jkr|
j| |
j|= d|
j_|

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

  t  t	d|
j  W q tk
r`   td|   Y qX qW ddiS )u   
        Метод вызывается из eva_app при синхронизации
        создает и привязывает пользователей
        https://bcrm.carbonsoft.ru/project/Document/DOC-003025#spec-0-ldap
        rT   r{   r3   r   rL   r   reg_org_name_listrp   r~   NZ	old_login)r3   r   )r3   u(   Создали пользователя rk   Zcmf_deletedu4   Пользователь {} удален из Eva {}Tr   u,   Добавляем пользователю u    права u    в Eva r   z
user_dict=zuser.groups=zuser.scope=u)   Не удалось обработать resultr   )r   r%   lowerrv   r   r3   r   r   r   rc   rH   rp   r~   r   r   r   r   formatremover   r   Zcommit_with_eventrL   rn   ro   )r2   r   r   r   r   rW   r{   Z	user_dictr3   _fieldsuserrp   r~   Zgrp_coder   r   r   rpc_account_sync_push3  sf    






zCmfAuth.rpc_account_sync_pushc            O   s  t js
d S dd }t jsd S | |}|d d }x|D ]}tjj|d d dgd}	|	sbt }	||	|d  |d d |	_|	  tjj|d dd	gd}
|
st }
||
| ||
_	|d |
_|	|
_
|
  q8W x.tjjd
ddd |D gdD ]}	|	  qW ddiS )Nc             S   sJ   xD|D ]<}|dks| ds|dr(qt| |rt| |||  qW d S )N)idcodeZext_ipZcmf_Z_id)rw   endswithhasattrsetattr)rz   Zobj_dictZ
field_namer   r   r   copy_fz  s    

z/CmfAuth.rpc_account_plugin_push.<locals>.copy_frT   r{   pluginr  r   )ext_idr   zplugin.*r	  zNOT INc             S   s   g | ]}|d  qS )r  r   )r   rz   r   r   r   r     s    z3CmfAuth.rpc_account_plugin_push.<locals>.<listcomp>)r   r   r   )r   ZIS_BOX_VERSIONr   r   Z	CmfPluginr   r	  rH   r   r{   r  r   delete)r2   r   Zauth_pluginsr   r   r  rW   r{   r   r  Zauth_plugin_objr   r   r   rpc_account_plugin_pushv  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   expiressecurehttponlysamesite)Zreauth_dater   r   Zaccess_token_expires_inrN   r   PROLONG_DAYSgetattrr   Z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   )rS   Tr  )r   r  r  r  r  )r  r^   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 )N)rS   nginx_auth_tokenpassword123Tr  )r   r  r  r  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   sf   t jjrd S tj } xJtjjdgdd| gddgd}|s<P x|D ]}d |_|	  qBW t
  qW d S )Nr   <r   d   )r   r   r   )rb   rx   r   r   r   r   r   r   r   rH   Z
cmf_commit)r   Zblocked_usersr   r   r   r   )cron_unblock_users_after_block_expiration  s    

z1CmfAuth.cron_unblock_users_after_block_expiration)r9   )T)r   N)r   N)N)N)r   NN)N)N)4__name__
__module____qualname__r$   Z	ts_lengthpropertyr   r!   r*   classmethodr8   rE   r>   rI   rV   staticmethodr?   rX   r%   r^   rg   rh   ri   r}   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rH   r   r   r   r  r  r  r  r  Zcmf_deferred_jobr   __classcell__r   r   )r   r   r      sd   


(Y


B"r   )#r   r   r/   rP   r   r"   r   r'   rB   r   Z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.includerp   r   r   r   Z
supervisorr   r   r   r   r   r   <module>   s*   