U
    v^Hx                     @   s  d dl mZ eZdddddddgZzd d	lmZ W n  ek
rR   d d	lmZ Y nX d d
l	Z	d d
l
Z
d dlmZ d d
lZd dlmZ d d
lZzd dlmZ W n  ek
r   d dlmZ Y nX zd dlmZ W n  ek
r   d dlmZ Y nX d d
lZd dlmZmZ zd d
lZW n ek
r2   d d
lZY nX d dlmZ eekrPeZ neZ d dl!m"Z" d dl#m$Z%m&Z&m'Z'm(Z( d dl)m*Z* dZ+dZ,dZ-dZ.dZ/e0e1e2fZ3dd Z4dd Z5G dd de'Z6G dd de%Z$G dd de%Z7G d d de8Z9G d!d" d"e9Z:G d#d$ d$e9Z;G d%d& d&e9Z<G d'd de8Z=G d(d) d)e=Z>G d*d de>Z?G d+d, d,e@ZAG d-d. d.eAZBG d/d0 d0eAZCG d1d2 d2eCZDG d3d4 d4eCZEG d5d6 d6eEZFG d7d8 d8eAZGG d9d: d:eAZHG d;d< d<eAZIG d=d> d>eAZJd
S )?    )print_functionAccessTokenAnonymousAccessToken AuthorizeRequestTokenWithBrowserCredentialStoreRequestTokenAuthorizationEngineConsumerCredentials)StringION)select)stdin)	urlencode)urljoin)	b64decode	b64encode)parse_qs)	HTTPError)r   r   OAuthAuthorizerSystemWideConsumer)urisz+request-tokenz+access-tokenz+authorize-token   i  c                   C   s   t tjddS )zWhether the user has disabled SSL certificate connection.

    Some testing servers have broken certificates.  Rather than raising an
    error, we allow an environment variable,
    ``LP_DISABLE_SSL_CERTIFICATE_VALIDATION`` to disable the check.
    Z%LP_DISABLE_SSL_CERTIFICATE_VALIDATIONF)boolosenvironget r   r   :/usr/lib/python3/dist-packages/launchpadlib/credentials.py$_ssl_certificate_validation_disabledY   s    
r   c                 C   sD   t  }tj|dj| d|t|d\}}|jdkr<t||||fS )zPOST to ``url`` with ``headers`` and a body of urlencoded ``params``.

    Wraps it up to make sure we avoid the SSL certificate validation if our
    environment tells us to.  Also, raises an error on non-200 statuses.
    )Z"disable_ssl_certificate_validationZPOST)methodheadersZbody   )r   httplib2ZHttpZrequestr   statusr   )urlr   paramsZcert_disabledresponsecontentr   r   r   
_http_postg   s       


r'   c                   @   sX   e Zd ZdZdZdZdZdZdZdd Z	e
d	d
 ZdejefddZejfddZdS )r	   zStandard credentials storage and usage class.

    :ivar consumer: The consumer (application)
    :type consumer: `Consumer`
    :ivar access_token: Access information on behalf of the user
    :type access_token: `AccessToken`
    NZuridictz<BR>
c                 C   s0   t  }| | | }t|tr,|d}|S )zeTurn this object into a string.

        This should probably be moved into OAuthAuthorizer.
        utf-8)r
   savegetvalue
isinstanceunicode_typeencode)selfZsio
serializedr   r   r   	serialize   s    


zCredentials.serializec                 C   s,   |  }t |ts|d}|t| |S )z}Create a `Credentials` object from a serialized string.

        This should probably be moved into OAuthAuthorizer.
        r*   )r-   r.   decodeloadr
   )clsvaluecredentialsr   r   r   from_string   s
    

zCredentials.from_stringc           	      C   s   | j dk	std| jdks$tdt|}t| j jddd}|t }d|i}|| jkrbd|d	< t	|||\}}t
|tr|d
}|| jkrt|}|dk	r||d< t|| _|S t|| _d|t| jjf }|dk	r|| j_|d| 7 }|S dS )a  Request an OAuth token to Launchpad.

        Also store the token in self._request_token.

        This method must not be called on an object with no consumer
        specified or if an access token has already been obtained.

        :param context: The context of this token, that is, its scope of
            validity within Launchpad.
        :param web_root: The URL of the website on which the token
            should be requested.
        :token_format: How the token should be
            presented. URI_TOKEN_FORMAT means just return the URL to
            the page that authorizes the token.  DICT_TOKEN_FORMAT
            means return a dictionary describing the token
            and the site's authentication policy.

        :return: If token_format is URI_TOKEN_FORMAT, the URL for the
            user to authorize the `AccessToken` provided by
            Launchpad. If token_format is DICT_TOKEN_FORMAT, a dict of
            information about the new access token.
        NzConsumer not specified.zAccess token already obtained.	PLAINTEXT&)oauth_consumer_keyoauth_signature_methodoauth_signatureRefererzapplication/jsonZAcceptr*   
lp.contextz%s%s?oauth_token=%sz&lp.context=%s)consumerAssertionErroraccess_tokenr   lookup_web_rootr(   keyrequest_token_pageDICT_TOKEN_FORMATr'   r-   bytesr3   jsonloadsr   from_params_request_tokenr8   authorize_token_pagecontext)	r0   rM   web_roottoken_formatr$   r#   r   r%   r&   r   r   r   get_request_token   s:    





zCredentials.get_request_tokenc                 C   sl   | j dk	stdt|}t| jjd| j jd| j j d}|t }d|i}t	|||\}}t
|| _dS )ad  Exchange the previously obtained request token for an access token.

        This method must not be called unless get_request_token() has been
        called and completed successfully.

        The access token will be stored as self.access_token.

        :param web_root: The base URL of the website that granted the
            request token.
        Nz5get_request_token() doesn't seem to have been called.r9   z&%s)r;   r<   oauth_tokenr=   r>   )rK   rA   r   rC   r(   r@   rD   secretaccess_token_pager'   r   r8   rB   )r0   rN   r$   r#   r   r%   r&   r   r   r   'exchange_request_token_for_access_token   s    

z3Credentials.exchange_request_token_for_access_token)__name__
__module____qualname____doc__rK   ZURI_TOKEN_FORMATrF   ZITEM_SEPARATORNEWLINEr2   classmethodr8   r   ZSTAGING_WEB_ROOTrP   rT   r   r   r   r   r	   v   s   

6c                   @   s(   e Zd ZdZedd Zedd ZdS )r   zAn OAuth access token.c                 C   s&   |d }|d }| d}| |||S )z:Create and return a new `AccessToken` from the given dict.rQ   oauth_token_secretr?   )r   )r5   r$   rD   rR   rM   r   r   r   rJ      s    
zAccessToken.from_paramsc                 C   s   t |ts|d}t|dd}|d }t|dks<td|d }|d }t|dks`td	|d }|d
}|dk	rt|dkstd|d }| |||S )z<Create and return a new `AccessToken` from the given string.r*   F)Zkeep_blank_valuesrQ   r   z/Query string must have exactly one oauth_token.r   r[   z*Query string must have exactly one secret.r?   Nz*Query string must have exactly one context)r-   r.   r3   r   lenrA   r   )r5   Zquery_stringr$   rD   rR   rM   r   r   r   r8      s$    


zAccessToken.from_stringN)rU   rV   rW   rX   rZ   rJ   r8   r   r   r   r   r      s
   
c                       s    e Zd ZdZ fddZ  ZS )r   zoAn OAuth access token that doesn't authenticate anybody.

    This token can be used for anonymous access.
    c                    s   t t| dd d S )N )superr   __init__r0   	__class__r   r   r_     s    zAnonymousAccessToken.__init__)rU   rV   rW   rX   r_   __classcell__r   r   ra   r   r     s   c                   @   s:   e Zd ZdZdddZdd Zdd Zd	d
 Zdd ZdS )r   zStore OAuth credentials locally.

    This is a generic superclass. To implement a specific way of
    storing credentials locally you'll need to subclass this class,
    and implement `do_save` and `do_load`.
    Nc                 C   s
   || _ dS )a  Constructor.

        :param credential_save_failed: A callback to be invoked if the
            save to local storage fails. You should never invoke this
            callback yourself! Instead, you should raise an exception
            from do_save().
        N)credential_save_failedr0   rd   r   r   r   r_     s    zCredentialStore.__init__c              
   C   sb   z|  || W nL tk
r&    Y n8 tk
r\ } z| jdkrD||   W 5 d}~X Y nX |S )zSave the credentials and invoke the callback on failure.

        Do not override this method when subclassing. Override
        do_save() instead.
        N)do_saveEXPLOSIVE_ERRORS	Exceptionrd   )r0   r7   unique_consumer_ider   r   r   r+   '  s    
zCredentialStore.savec                 C   s
   t  dS )zStore newly-authorized credentials locally for later use.

        :param credentials: A Credentials object to save.
        :param unique_consumer_id: A string uniquely identifying an
            OAuth consumer on a Launchpad instance.
        NNotImplementedError)r0   r7   ri   r   r   r   rf   7  s    zCredentialStore.do_savec                 C   s
   |  |S )a0  Retrieve credentials from a local store.

        This method is the inverse of `save`.

        There's no special behavior in this method--it just calls
        `do_load`. There _is_ special behavior in `save`, and this
        way, developers can remember to implement `do_save` and
        `do_load`, not `do_save` and `load`.

        :param unique_key: A string uniquely identifying an OAuth consumer
            on a Launchpad instance.

        :return: A `Credentials` object if one is found in the local
            store, and None otherise.
        )do_loadr0   
unique_keyr   r   r   r4   @  s    zCredentialStore.loadc                 C   s
   t  dS )a@  Retrieve credentials from a local store.

        This method is the inverse of `do_save`.

        :param unique_key: A string uniquely identifying an OAuth consumer
            on a Launchpad instance.

        :return: A `Credentials` object if one is found in the local
            store, and None otherise.
        Nrk   rn   r   r   r   rm   R  s    zCredentialStore.do_load)N)	rU   rV   rW   rX   r_   r+   rf   r4   rm   r   r   r   r   r     s   

	c                       sB   e Zd ZdZdZd fdd	Zedd Zd	d
 Zdd Z	  Z
S )KeyringCredentialStorezStore credentials in the GNOME keyring or KDE wallet.

    This is a good solution for desktop applications and interactive
    scripts. It doesn't work for non-interactive scripts, or for
    integrating third-party websites into Launchpad.
    s   <B64>NFc                    s(   t t| | d | _|r$t|| _d S N)r^   rp   r_   	_fallbackMemoryCredentialStore)r0   rd   Zfallbackra   r   r   r_   j  s    zKeyringCredentialStore.__init__c                   C   sJ   dt  krddladt  krFzddlma W n tk
rD   taY nX dS )aG  Ensure the keyring module is imported (postponing side effects).

        The keyring module initializes the environment-dependent backend at
        import time (nasty).  We want to avoid that initialization because it
        may do things like prompt the user to unlock their password store
        (e.g., KWallet).
        keyringr   NNoKeyringError)ru   )globalsrt   Zkeyring.errorsru   ImportErrorRuntimeErrorr   r   r   r   _ensure_keyring_importedp  s    	

z/KeyringCredentialStore._ensure_keyring_importedc              
   C   s   |    | }| jt| }ztd||d W nP tk
r } z2ttkr^dt	|kr^ | j
rt| j
|| n W 5 d}~X Y nX dS )z2Store newly-authorized credentials in the keyring.launchpadlibr*   $No recommended backend was availableN)ry   r2   	B64MARKERr   rt   Zset_passwordr3   ru   rx   strrr   r+   )r0   r7   ro   r1   rj   r   r   r   rf     s"      
zKeyringCredentialStore.do_savec              
   C   s   |    ztd|}W nT tk
rl } z6ttkr@dt|kr@ | jrZ| j| W Y S  W 5 d}~X Y nX |dk	rt|t	r|
d}|| jrzt|t| jd }W n tk
r   Y dS X zt|}|W S    Y dS X dS )z&Retrieve credentials from the keyring.rz   r{   Nutf8)ry   rt   Zget_passwordru   rx   r}   rr   r4   r-   r.   r/   
startswithr|   r   r\   	TypeErrorr	   r8   )r0   ro   Zcredential_stringrj   r7   r   r   r   rm     s<     



zKeyringCredentialStore.do_load)NF)rU   rV   rW   rX   r|   r_   staticmethodry   rf   rm   rc   r   r   ra   r   rp   `  s   
rp   c                       s2   e Zd ZdZd	 fdd	Zdd Zdd Z  ZS )
UnencryptedFileCredentialStorezStore credentials unencrypted in a file on disk.

    This is a good solution for scripts that need to run without any
    user interaction.
    Nc                    s   t t| | || _d S rq   )r^   r   r_   filename)r0   r   rd   ra   r   r   r_     s    
z'UnencryptedFileCredentialStore.__init__c                 C   s   | | j dS )zSave the credentials to disk.N)Zsave_to_pathr   r0   r7   ro   r   r   r   rf     s    z&UnencryptedFileCredentialStore.do_savec                 C   s4   t j| jr0t | jtj dks0t| jS dS )zLoad the credentials from disk.r   N)r   pathexistsr   statST_SIZEr	   Zload_from_pathrn   r   r   r   rm     s
    z&UnencryptedFileCredentialStore.do_load)NrU   rV   rW   rX   r_   rf   rm   rc   r   r   ra   r   r     s   r   c                       s2   e Zd ZdZd	 fdd	Zdd Zdd Z  ZS )
rs   zCredentialStore that stores keys only in memory.

    This can be used to provide a CredentialStore instance without
    actually saving any key to persistent storage.
    Nc                    s   t t| | i | _d S rq   )r^   rs   r_   _credentialsre   ra   r   r   r_     s    zMemoryCredentialStore.__init__c                 C   s   || j |< dS )z!Store the credentials in our dictN)r   r   r   r   r   rf     s    zMemoryCredentialStore.do_savec                 C   s   | j |S )z&Retrieve the credentials from our dict)r   r   rn   r   r   r   rm     s    zMemoryCredentialStore.do_load)Nr   r   r   ra   r   rs     s   rs   c                   @   sJ   e Zd ZdZdZdddZedd Zdd	 Zd
d Z	dd Z
dd ZdS )r   a/  The superclass of all request token authorizers.

    This base class does not implement request token authorization,
    since that varies depending on how you want the end-user to
    authorize a request token. You'll need to subclass this class and
    implement `make_end_user_authorize_token`.
    ZUNAUTHORIZEDNc                 C   s   t || _t || _|dkr0|dkr0td|dk	rP|dk	rPtd||f |dkrhdg}t|}nt|}|}|| _|| _	|pg | _
dS )aD  Base class initialization.

        :param service_root: The root of the Launchpad instance being
            used.

        :param application_name: The name of the application that
            wants to use launchpadlib. This is used in conjunction
            with a desktop-wide integration.

            If you specify this argument, your values for
            consumer_name and allow_access_levels are ignored.

        :param consumer_name: The OAuth consumer name, for an
            application that wants its own point of integration into
            Launchpad. In almost all cases, you want to specify
            application_name instead and do a desktop-wide
            integration. The exception is when you're integrating a
            third-party website into Launchpad.

        :param allow_access_levels: A list of the Launchpad access
            levels to present to the user. ('READ_PUBLIC' and so on.)
            Your value for this argument will be ignored during a
            desktop-wide integration.
        :type allow_access_levels: A list of strings.
        Nz:You must provide either application_name or consumer_name.zZYou must provide only one of application_name and consumer_name. (You provided %r and %r.)ZDESKTOP_INTEGRATION)r   Zlookup_service_rootservice_rootZweb_root_for_service_rootrN   
ValueErrorr   r   r@   application_nameallow_access_levels)r0   r   r   consumer_namer   r@   r   r   r   r_     s,     
z(RequestTokenAuthorizationEngine.__init__c                 C   s   | j jd | j S )z7Return a string identifying this consumer on this host.@)r@   rD   r   r`   r   r   r   ri   .  s    z2RequestTokenAuthorizationEngine.unique_consumer_idc                 C   s>   dt |f }d}t| jdkr2|||| j 7 }t| j|S )zReturn the authorization URL for a request token.

        This is the URL the end-user must visit to authorize the
        token. How exactly does this happen? That depends on the
        subclass implementation.
        z%s?oauth_token=%sz&allow_permission=r   )rL   r\   r   joinr   rN   )r0   request_tokenZpageZallow_permissionr   r   r   authorization_url3  s    
z1RequestTokenAuthorizationEngine.authorization_urlc                 C   s6   |  |}| || |jdkr$dS ||| j |S )ad  Authorize a token and associate it with the given credentials.

        If the credential store runs into a problem storing the
        credential locally, the `credential_save_failed` callback will
        be invoked. The callback will not be invoked if there's a
        problem authorizing the credentials.

        :param credentials: A `Credentials` object. If the end-user
            authorizes these credentials, this object will have its
            .access_token property set.

        :param credential_store: A `CredentialStore` object. If the
            end-user authorizes the credentials, they will be
            persisted locally using this object.

        :return: If the credentials are successfully authorized, the
            return value is the `Credentials` object originally passed
            in. Otherwise the return value is None.
        N)rP   make_end_user_authorize_tokenrB   r+   ri   )r0   r7   Zcredential_storeZrequest_token_stringr   r   r   __call__B  s    

z(RequestTokenAuthorizationEngine.__call__c                 C   s   |j | jtjd}|d S )z\Get a new request token from the server.

        :param return: The request token.
        )rN   rO   rQ   )rP   rN   r	   rF   )r0   r7   Zauthorization_jsonr   r   r   rP   `  s
    z1RequestTokenAuthorizationEngine.get_request_tokenc                 C   s
   t  dS )a5  Authorize the given request token using the given credentials.

        Your subclass must implement this method: it has no default
        implementation.

        Because an access token may expire or be revoked in the middle
        of a session, this method may be called at arbitrary points in
        a launchpadlib session, or even multiple times during a single
        session (with a different request token each time).

        In most cases, however, this method will be called at the
        beginning of a launchpadlib session, or not at all.
        Nrk   )r0   r7   r   r   r   r   r   j  s    z=RequestTokenAuthorizationEngine.make_end_user_authorize_token)NNN)rU   rV   rW   rX   ZUNAUTHORIZED_ACCESS_LEVELr_   propertyri   r   r   rP   r   r   r   r   r   r     s       
9

c                   @   s@   e Zd ZdZdZdZdd Zdd Zdd	 Zd
d Z	dd Z
dS )AuthorizeRequestTokenWithURLzAuthorize using a URL.

    This authorizer simply shows the URL for the user to open for
    authorization, and waits until the server responds.
    zPlease open this authorization page:
 (%s)
in your browser. Use your browser to authorize
this program to access Launchpad on your behalf.z.Press Enter after authorizing in your browser.c                 C   s   t | dS )zDisplay a message.

        By default, prints the message to standard output. The message
        does not require any user interaction--it's solely
        informative.
        N)print)r0   messager   r   r   output  s    z#AuthorizeRequestTokenWithURL.outputc                 C   s   |  | j|  dS )Notify the end-user of the URL.N)r   WAITING_FOR_USER)r0   r   r   r   r   !notify_end_user_authorization_url  s    z>AuthorizeRequestTokenWithURL.notify_end_user_authorization_urlc              
   C   s|   z| | j W n` tk
rp } zB|jjdkr:t|jn&|jjdkrVtd t| t|jW 5 d}~X Y nX |j	dk	S )z Check if the end-user authorizedi  i  z#Unexpected response from Launchpad:N)
rT   rN   r   r%   r"   EndUserDeclinedAuthorizationr&   r   EndUserNoAuthorizationrB   )r0   r7   rj   r   r   r   check_end_user_authorization  s    z9AuthorizeRequestTokenWithURL.check_end_user_authorizationc                 C   s"   |  | j t  | | dS )"Wait for the end-user to authorizeN)r   WAITING_FOR_LAUNCHPADr   readliner   )r0   r7   r   r   r   wait_for_end_user_authorization  s    z<AuthorizeRequestTokenWithURL.wait_for_end_user_authorizationc                 C   s"   |  |}| | | | dS )z2Have the end-user authorize the token using a URL.N)r   r   r   )r0   r7   r   r   r   r   r   r     s    

z:AuthorizeRequestTokenWithURL.make_end_user_authorize_tokenN)rU   rV   rW   rX   r   r   r   r   r   r   r   r   r   r   r   r   {  s   	r   c                       sJ   e Zd ZdZdZdZdZdZdZd fdd		Z	 fd
dZ
dd Z  ZS )r   aS  Authorize using a URL that pops-up automatically in a browser.

    This authorizer simply opens up the end-user's web browser to a
    Launchpad URL and lets the end-user authorize the request token
    themselves.

    This is the same as its superclass, except this class also
    performs the browser automatic opening of the URL.
    zThe authorization page:
 (%s)
should be opening in your browser. Use your browser to authorize
this program to access Launchpad on your behalf.z/Press Enter to continue or wait (%d) seconds...   )zwww-browserZlinksZlinks2ZlynxZelinkszelinks-liteZnetrikZw3mz5Waiting to hear from Launchpad about your decision...Nc                    s   t t| ||d| dS )ao  Constructor.

        :param service_root: See `RequestTokenAuthorizationEngine`.
        :param application_name: See `RequestTokenAuthorizationEngine`.
        :param consumer_name: The value of this argument is
            ignored. If we have the capability to open the end-user's
            web browser, we must be running on the end-user's computer,
            so we should do a full desktop integration.
        :param credential_save_failed: See `RequestTokenAuthorizationEngine`.
        :param allow_access_levels: The value of this argument is
            ignored, for the same reason as consumer_name.
        N)r^   r   r_   )r0   r   r   r   rd   r   ra   r   r   r_     s    
  z)AuthorizeRequestTokenWithBrowser.__init__c                    s   t t| | z"t }t|dd}|| jk}W n tjk
rP   d}d}Y nX |r| | j	| j
  ttgg g | j
\}}}|rt  |dk	rt| dS )r   basenameNF)r^   r   r   
webbrowserr   getattrTERMINAL_BROWSERSErrorr   TIMEOUT_MESSAGETIMEOUTr   r   r   open)r0   r   Zbrowser_objZbrowserZconsole_browserZrlist_ra   r   r   r     s&    
zBAuthorizeRequestTokenWithBrowser.notify_end_user_authorization_urlc                 C   sr   |  | j t }|jdkrntt z| |r8W qnW n tk
rN   Y nX t |t krt	dt qdS )r   NzTimed out after %d seconds.)
r   r   timerB   sleepaccess_token_poll_timer   r   access_token_poll_timeoutTokenAuthorizationTimedOut)r0   r7   Z
start_timer   r   r   r     s    


z@AuthorizeRequestTokenWithBrowser.wait_for_end_user_authorization)NNN)rU   rV   rW   rX   r   r   r   r   r   r_   r   r   rc   r   r   ra   r   r     s       c                   @   s   e Zd ZdS )TokenAuthorizationExceptionNrU   rV   rW   r   r   r   r   r     s   r   c                   @   s   e Zd ZdS )RequestTokenAlreadyAuthorizedNr   r   r   r   r   r     s   r   c                   @   s   e Zd ZdZdS )EndUserAuthorizationFailedz?Superclass exception for all failures of end-user authorizationNrU   rV   rW   rX   r   r   r   r   r     s   r   c                   @   s   e Zd ZdZdS )r   zEnd-user declined authorizationNr   r   r   r   r   r     s   r   c                   @   s   e Zd ZdZdS )r   z*End-user did not perform any authorizationNr   r   r   r   r   r     s   r   c                   @   s   e Zd ZdZdS )r   z<End-user did not perform any authorization in timeout periodNr   r   r   r   r   r   "  s   r   c                   @   s   e Zd ZdS )ClientErrorNr   r   r   r   r   r   '  s   r   c                   @   s   e Zd ZdS )ServerErrorNr   r   r   r   r   r   +  s   r   c                   @   s   e Zd ZdS )NoLaunchpadAccountNr   r   r   r   r   r   /  s   r   c                   @   s   e Zd ZdS )TooManyAuthenticationFailuresNr   r   r   r   r   r   3  s   r   )KZ
__future__r   typeZ__metaclass____all__	cStringIOr
   rw   ior!   r   r   r   sysr   r   Zurllib.parser   Zurllibr   Zurlparser   base64r   r   rH   Z
simplejsonZsix.moves.urllib.parser   rG   r}   Zunicoder.   Zlazr.restfulclient.errorsr   Z"lazr.restfulclient.authorize.oauthr   Z_AccessTokenr   r   r   rz   r   rE   rS   rL   r   r   MemoryErrorKeyboardInterrupt
SystemExitrg   r   r'   r	   r   objectr   rp   r   rs   r   r   r   rh   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   


v 	K_ <T