3
ãdeþI  ã               @   sŒ  d 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ZddlZy4ddl	m
Z
mZ ddlmZmZmZ ddlmZ W nL ek
rÀ   ddlm
Z
mZ ddlmZmZ ddlmZ ddlmZ Y nX ejeƒZdEd	d
„Zdd„ Zdd„ ZdFdd„Zdd„ Zdd„ Zdd„ ZG dd„ deƒZ G dd„ de
e!ƒZ"G dd„ de"ƒZ#G dd„ de!ƒZ$edk�rˆddl%Z%ddl&Z&d d!l'm(Z( ej)ej*d"� e%j+e%j,e d# d$� Z-Z.e-j/d%d&d'd(� e-j/d)d*d+� e-j/d,e0dd-d.� e-j/d/e0d0d1d.� e-j/d2d3d4d5� e-j/d6dd7d5� e.j1ƒ Z2e(d8e2j3ie2j4ƒZ5e$e2j6d9��dZ7e5j8e2j9�r6e2j9j:ƒ ndd:j;e2j<e7j=ƒ d;�d<�Z>e?e&j@e7jAe>d= d>d?d@e2jBe>dA dB�dCdD�ƒ W dQ R X dS )Ga$  A one-stop helper for desktop app to acquire an authorization code.

It starts a web server to listen redirect_uri, waiting for auth code.
It optionally opens a browser window to guide a human user to manually login.
After obtaining an auth code, the web server will automatically shut down.
é    N)ÚTemplate)Ú
HTTPServerÚBaseHTTPRequestHandler)ÚurlparseÚparse_qsÚ	urlencode)Úescape)r   r   )r   c             C   s*   t | d��}|j|dd�jdƒS Q R X d S )N)Úportz×<html><body>
                Open this link to <a href='$auth_uri'>Sign In</a>
                (You may want to use incognito window)
                <hr><a href='$abort_uri'>Abort</a>
                </body></html>)Úauth_uriÚwelcome_templateÚcode)ÚAuthCodeReceiverÚget_auth_responseÚget)Zlisten_portr
   Úreceiver© r   úN/var/www/agendate/envp3/lib/python3.6/site-packages/msal/oauth2cli/authcode.pyÚobtain_auth_code   s
    r   c              C   sp   yNt dƒ�<} x4| jƒ D ](}|jddƒd jƒ }|jƒ dkrdS qW W d Q R X W n tk
rb   Y nX tjjdƒS )Nz/proc/1/cgroupú:é   ú/Tz/.dockerenv)ÚopenÚ	readlinesÚsplitÚstripÚIOErrorÚosÚpathÚexists)ÚfÚlineZcgroup_pathr   r   r   Ú_is_inside_docker*   s    
r!   c              C   sH   dd l } | jƒ }t|d|d ƒjƒ }t|d|d ƒjƒ }|dkoFd|kS )Nr   ÚsystemÚreleaser   ÚlinuxZ	microsoft)ÚplatformÚunameÚgetattrÚlower)r%   r&   Zplatform_namer#   r   r   r   Úis_wsl8   s
    r)   c             C   sz   ddl }|r|j|ƒj| ƒ}n
|j| ƒ}| rvtƒ rvy,ddl}|jddddj| ƒgƒ}|dk}W n tk
rt   Y nX |S )zJBrowse uri with named browser. Default browser is customizable by $BROWSERr   Nzpowershell.exez
-NoProfilez-CommandzStart-Process "{}")Ú
webbrowserr   r   r)   Ú
subprocessÚcallÚformatÚFileNotFoundError)r
   Úbrowser_namer*   Úbrowser_openedr+   Z	exit_coder   r   r   Ú_browseD   s    
r1   c             C   s   dd„ | j ƒ D ƒS )z;Flatten parse_qs()'s single-item lists into the item itselfc             S   s4   i | ],\}}t |tƒr*t|ƒd kr*|d n||“qS )é   r   )Ú
isinstanceÚlistÚlen)Ú.0ÚkÚvr   r   r   ú
<dictcomp>]   s   z_qs2kv.<locals>.<dictcomp>)Úitems)Úqsr   r   r   Ú_qs2kv[   s    r<   c             C   s
   | j dƒS )Nú<)Ú
startswith)Útextr   r   r   Ú_is_htmla   s    r@   c             C   s   dd„ | j ƒ D ƒS )Nc             S   s   i | ]\}}t |ƒ|“qS r   )r   )r6   r7   r8   r   r   r   r9   f   s    z_escape.<locals>.<dictcomp>)r:   )Zkey_value_pairsr   r   r   Ú_escapee   s    rA   c               @   s&   e Zd Zdd„ Zd	dd„Zdd„ ZdS )
Ú_AuthCodeHandlerc             C   s¼   t t| jƒjƒ}|jdƒs$|jdƒrªt|ƒ}tjd|ƒ | jj	r^| jj	|jdƒkr^| j
dƒ q¸d|krn| jjn| jj}t|jƒrŠt|ƒ}n|}| j
|jf |Žƒ || j_n| j
| jjƒ d S )Nr   ÚerrorzGot auth response: %sÚstatezState mismatch)r   r   r   Úqueryr   r<   ÚloggerÚdebugÚserverÚ
auth_stateÚ_send_full_responseÚsuccess_templateÚerror_templater@   ÚtemplaterA   Úsafe_substituteÚauth_responseÚwelcome_page)Úselfr;   rO   rM   Z	safe_datar   r   r   Údo_GETj   s    


z_AuthCodeHandler.do_GETTc             C   sL   | j |rdndƒ t|ƒrdnd}| jd|ƒ | jƒ  | jj|jdƒƒ d S )NéÈ   i�  z	text/htmlz
text/plainzContent-typezutf-8)Zsend_responser@   Zsend_headerZend_headersÚwfileÚwriteÚencode)rQ   ÚbodyZis_okÚcontent_typer   r   r   rJ   ‚   s
    z$_AuthCodeHandler._send_full_responsec             G   s   t j|f|žŽ  d S )N)rF   rG   )rQ   r-   Úargsr   r   r   Úlog_message‰   s    z_AuthCodeHandler.log_messageN)T)Ú__name__Ú
__module__Ú__qualname__rR   rJ   rZ   r   r   r   r   rB   i   s   
rB   c                   s$   e Zd Z‡ fdd„Zdd„ Z‡  ZS )Ú_AuthCodeHttpServerc                s>   |\}}|r"t jdkstƒ r"d| _tt| ƒj|f|ž|Ž d S )NÚwin32F)Úsysr%   r)   Úallow_reuse_addressÚsuperr^   Ú__init__)rQ   Úserver_addressrY   ÚkwargsÚ_r	   )Ú	__class__r   r   rc   Ž   s    z_AuthCodeHttpServer.__init__c             C   s   t dƒ‚d S )Nz"Timeout. No auth response arrived.)ÚRuntimeError)rQ   r   r   r   Úhandle_timeout˜   s    z"_AuthCodeHttpServer.handle_timeout)r[   r\   r]   rc   ri   Ú__classcell__r   r   )rg   r   r^   �   s   
r^   c               @   s   e Zd ZejZdS )Ú_AuthCodeHttpServer6N)r[   r\   r]   ÚsocketÚAF_INET6Úaddress_familyr   r   r   r   rk   ¡   s   rk   c               @   sJ   e Zd Zddd„Zdd„ Zddd„Zddd	„Zd
d„ Zdd„ Zdd„ Z	dS )r   Nc             C   sJ   t ƒ r
dnd}t|pg ƒ| _d|kr(tnt}|||p6dftƒ| _d| _dS )aø  Create a Receiver waiting for incoming auth response.

        :param port:
            The local web server will listen at http://...:<port>
            You need to use the same port when you register with your app.
            If your Identity Provider supports dynamic port, you can use port=0 here.
            Port 0 means to use an arbitrary unused port, per this official example:
            https://docs.python.org/2.7/library/socketserver.html#asynchronous-mixins

        :param scheduled_actions:
            For example, if the input is
            ``[(10, lambda: print("Got stuck during sign in? Call 800-000-0000"))]``
            then the receiver would call that lambda function after
            waiting the response for 10 seconds.
        z0.0.0.0z	127.0.0.1r   r   FN)r!   ÚsortedÚ_scheduled_actionsrk   r^   rB   Ú_serverÚ_closing)rQ   r	   Zscheduled_actionsÚaddressÚServerr   r   r   rc   §   s
    zAuthCodeReceiver.__init__c             C   s   | j jd S )z*The port this server actually listening tor2   )rq   rd   )rQ   r   r   r   Úget_portÊ   s    zAuthCodeReceiver.get_portc             K   s¤   i }t j| j|f|d�}d|_|jƒ  tjƒ }xl|rDtjƒ | |k ndrštjdƒ |jƒ s\P x:| jr–tjƒ | | jd d kr–| jj	dƒ\}}|ƒ  q^W q0W |p¢dS )a­  Wait and return the auth response. Raise RuntimeError when timeout.

        :param str auth_uri:
            If provided, this function will try to open a local browser.
        :param int timeout: In seconds. None means wait indefinitely.
        :param str state:
            You may provide the state you used in auth_uri,
            then we will use it to validate incoming response.
        :param str welcome_template:
            If provided, your end user will see it instead of the auth_uri.
            When present, it shall be a plaintext or html template following
            `Python Template string syntax <https://docs.python.org/3/library/string.html#template-strings>`_,
            and include some of these placeholders: $auth_uri and $abort_uri.
        :param str success_template:
            The page will be displayed when authentication was largely successful.
            Placeholders can be any of these:
            https://tools.ietf.org/html/rfc6749#section-5.1
        :param str error_template:
            The page will be displayed when authentication encountered error.
            Placeholders can be any of these:
            https://tools.ietf.org/html/rfc6749#section-5.2
        :param callable auth_uri_callback:
            A function with the shape of lambda auth_uri: ...
            When a browser was unable to be launch, this function will be called,
            so that the app could tell user to manually visit the auth_uri.
        :param str browser_name:
            If you did
            ``webbrowser.register("xyz", None, BackgroundBrowser("/path/to/browser"))``
            beforehand, you can pass in the name "xyz" to use that browser.
            The default value ``None`` means using default browser,
            which is customizable by env var $BROWSER.
        :return:
            The auth response of the first leg of Auth Code flow,
            typically {"code": "...", "state": "..."} or {"error": "...", ...}
            See https://tools.ietf.org/html/rfc6749#section-4.1.2
            and https://openid.net/specs/openid-connect-core-1_0.html#AuthResponse
            Returns None when the state was mismatched, or when timeout occurred.
        )ÚtargetrY   re   Tr2   r   N)
Ú	threadingÚThreadÚ_get_auth_responseÚdaemonÚstartÚtimeÚsleepÚis_aliverp   Úpop)rQ   Útimeoutre   ÚresultÚtÚbeginrf   Úcallbackr   r   r   r   Ï   s    5
z"AuthCodeReceiver.get_auth_responsec
             C   s$  dj | jƒ d�}
dj |
d�}tjd|ƒ t|p0dƒj||d�| j_|r¸|rN|
n|}tjd| ƒ d	}yt	||	d
�}W n   tj
dƒ Y nX |s¸|s°tjdj ||| jƒ d�ƒ n||ƒ t|pÀdƒ| j_t|pÐdƒ| j_|| j_i | j_|| j_x | j�s| jjƒ  | jjròP qòW |j| jjƒ d S )Nzhttp://localhost:{p})Úpz{loc}?error=abort)ÚloczAbort by visit %sÚ )r
   Ú	abort_uriz*Open a browser on this device to visit: %sF)r/   z_browse(...) unsuccessfulaç  Found no browser in current environment. If this program is being run inside a container which either (1) has access to host network (i.e. started by `docker run --net=host -it ...`), or (2) published port {port} to host network (i.e. started by `docker run -p 127.0.0.1:{port}:{port} -it ...`), you can use browser on host to visit the following link. Otherwise, this auth attempt would either timeout (current timeout setting is {timeout}) or be aborted by CTRL+C. Auth URI: {auth_uri})r
   r€   r	   z8Authentication completed. You can close this window now.z?Authentication failed. $error: $error_description. ($error_uri))r-   ru   rF   rG   r   rN   rq   rP   Úinfor1   Ú	exceptionÚwarningrK   rL   r€   rO   rI   rr   Úhandle_requestÚupdate)rQ   r�   r
   r€   rD   r   rK   rL   Zauth_uri_callbackr/   Zwelcome_urirˆ   Z_urir0   r   r   r   ry     s>    




z#AuthCodeReceiver._get_auth_responsec             C   s   d| _ | jjƒ  dS )zGEither call this eventually; or use the entire class as context managerTN)rr   rq   Úserver_close)rQ   r   r   r   ÚcloseJ  s    zAuthCodeReceiver.closec             C   s   | S )Nr   )rQ   r   r   r   Ú	__enter__O  s    zAuthCodeReceiver.__enter__c             C   s   | j ƒ  d S )N)r�   )rQ   Úexc_typeÚexc_valÚexc_tbr   r   r   Ú__exit__R  s    zAuthCodeReceiver.__exit__)NN)N)NNNNNNNN)
r[   r\   r]   rc   ru   r   ry   r�   r�   r”   r   r   r   r   r   ¥   s   
#
E   
3r   Ú__main__r2   )ÚClient)Úlevelz/The auth code received will be shown at stdout.)Úformatter_classÚdescriptionz
--endpointzThe auth endpoint for your app.z>https://login.microsoftonline.com/common/oauth2/v2.0/authorize)ÚhelpÚdefaultÚ	client_idz!The client_id of your application)rš   z--portzThe port in redirect_uri)Útyper›   rš   z	--timeouté<   zTimeout value, in secondz--hostz	127.0.0.1zThe host of redirect_uri)r›   rš   z--scopezThe scope listZauthorization_endpoint)r	   zhttp://{h}:{p})Úhr…   )ÚscopeZredirect_urir
   zA<a href='$auth_uri'>Sign In</a>, or <a href='$abort_uri'>Abort</az<html>Oh no. $error</html>zOh yeah. Got $coderD   )r
   r   rL   rK   r€   rD   é   )Úindent)N)N)CÚ__doc__Úloggingr   rl   r`   Ústringr   rw   r|   Zhttp.serverr   r   Úurllib.parser   r   r   Úhtmlr   ÚImportErrorÚBaseHTTPServerÚurllibÚcgiÚ	getLoggerr[   rF   r   r!   r)   r1   r<   r@   rA   rB   Úobjectr^   rk   r   ÚargparseÚjsonZoauth2r–   ÚbasicConfigÚINFOÚArgumentParserÚArgumentDefaultsHelpFormatterr…   ÚparserÚadd_argumentÚintÚ
parse_argsrY   Zendpointrœ   Úclientr	   r   Zinitiate_auth_code_flowr    r   r-   Úhostru   ZflowÚprintÚdumpsr   r€   r   r   r   r   Ú<module>   st   


$ 3


