3
Zig              $   @   s  d dl mZ eZdddddddgZd d	lZyd d
lmZ W n  ek
rZ   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yd dlmZ W n  ek
r   d dlmZ Y nX y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 yd d	lZW n ek
r:   d d	lZY nX eekrLe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-d7Z.e/e0e1fZ2dd Z3dd Z4G dd de&Z5G dd de$Z#G dd de$Z6G d d de7Z8G d!d" d"e8Z9G d#d$ d$e8Z:G d%d de7Z;G d&d de;Z<G d'd( d(e=Z>G d)d* d*e>Z?G d+d, d,e>Z@G d-d. d.e>ZAG d/d0 d0e>ZBG d1d2 d2e>ZCG d3d4 d4e>ZDG d5d6 d6e>ZEd	S )8    )print_functionAccessTokenAnonymousAccessToken AuthorizeRequestTokenWithBrowserCredentialStoreRequestTokenAuthorizationEngineConsumerCredentialsN)StringIO)select)stdin)	urlencode)urljoin)	b64decode	b64encode)	HTTPError)r   r   OAuthAuthorizerSystemWideConsumer)urisz+request-tokenz+access-tokenz+authorize-token      <   c               C   s   t tj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_disabledX   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_postf   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  }| j| |j }t|tr,|jd}|S )zeTurn this object into a string.

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

        This should probably be moved into OAuthAuthorizer.
        zutf-8)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j|}t| j jddd}|t }d|i}|| jkrbd|d	< t	|||\}}t
|tr|jd
}|| jkrtj|}|dk	r||d< tj|| _|S tj|| _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Acceptzutf-8z
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   rL   web_roottoken_formatr%   r$   r    r&   r'   r   r   r   get_request_token   s6    





zCredentials.get_request_tokenc             C   sl   | j dk	stdtj|}t| jjd| j jd| j j d}|t }d|i}t	|||\}}t
j|| _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>   )rJ   r@   r   rB   r)   r?   rC   secretaccess_token_pager(   r   r8   rA   )r0   rM   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__rJ   ZURI_TOKEN_FORMATrE   ZITEM_SEPARATORNEWLINEr2   classmethodr8   r   ZSTAGING_WEB_ROOTrO   rS   r   r   r   r   r	   u   s   5c               @   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 }|j d}| |||S )z:Create and return a new `AccessToken` from the given dict.rP   oauth_token_secretz
lp.context)r   )r5   r%   rC   rQ   rL   r   r   r   rI      s    
zAccessToken.from_paramsc             C   s   t |ts|jd}tj|dd}|d }t|dks>td|d }|d }t|dksbtd	|d }|jd
}|dk	rt|dkstd|d }| |||S )z<Create and return a new `AccessToken` from the given string.zutf-8F)Zkeep_blank_valuesrP   r   z/Query string must have exactly one oauth_token.r   rZ   z*Query string must have exactly one secret.z
lp.contextNz*Query string must have exactly one context)r-   r.   r3   cgiZparse_qslenr@   r   )r5   Zquery_stringr%   rC   rQ   rL   r   r   r   r8      s     


zAccessToken.from_stringN)rT   rU   rV   rW   rY   rI   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| jdd d S )N )superr   __init__)r0   )	__class__r   r   r_     s    zAnonymousAccessToken.__init__)rT   rU   rV   rW   r_   __classcell__r   r   )r`   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   rb   r   r   r   r_     s    zCredentialStore.__init__c             C   sb   y| j || W nL tk
r&    Y n8 tk
r\ } z| jdkrD|| j  W Y dd}~X 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	Exceptionrb   )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   rf   r   r   r   rc   6  s    zCredentialStore.do_savec             C   s
   | j |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.
        N)rh   )r0   rj   r   r   r   ri   Q  s    zCredentialStore.do_load)N)	rT   rU   rV   rW   r_   r+   rc   r4   ri   r   r   r   r   r     s   

	c               @   s0   e Zd ZdZdZedd Zdd Zdd Zd	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>c               C   s   dt  krddla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)globalsrl   r   r   r   r   _ensure_keyring_importedi  s    	
z/KeyringCredentialStore._ensure_keyring_importedc             C   s6   | j   |j }| jt| }tjd||jd dS )z2Store newly-authorized credentials in the keyring.launchpadlibzutf-8N)rn   r2   	B64MARKERr   rl   Zset_passwordr3   )r0   r7   rj   r1   r   r   r   rc   v  s
    zKeyringCredentialStore.do_savec             C   s   | j   tjd|}|dk	rt|tr0|jd}|j| jrlyt|t	| jd }W n t
k
rj   dS X ytj|}|S    dS dS )z&Retrieve credentials from the keyring.ro   Nutf8)rn   rl   Zget_passwordr-   r.   r/   
startswithrp   r   r\   	TypeErrorr	   r8   )r0   rj   Zcredential_stringr7   r   r   r   ri     s$    


zKeyringCredentialStore.do_loadN)	rT   rU   rV   rW   rp   staticmethodrn   rc   ri   r   r   r   r   rk   _  s
   rk   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| j| || _d S )N)r^   ru   r_   filename)r0   rv   rb   )r`   r   r   r_     s    
z'UnencryptedFileCredentialStore.__init__c             C   s   |j | j dS )zSave the credentials to disk.N)Zsave_to_pathrv   )r0   r7   rj   r   r   r   rc     s    z&UnencryptedFileCredentialStore.do_savec             C   s6   t jj| jr2t j| jtj dk r2tj| jS dS )zLoad the credentials from disk.r   N)r   pathexistsrv   statST_SIZEr	   Zload_from_path)r0   rj   r   r   r   ri     s    z&UnencryptedFileCredentialStore.do_load)N)rT   rU   rV   rW   r_   rc   ri   ra   r   r   )r`   r   ru     s   ru   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 j|| _t j|| _|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_rootrM   
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?   rC   r{   )r0   r   r   r   rf     s    z2RequestTokenAuthorizationEngine.unique_consumer_idc             C   s>   dt |f }d}t| jdkr2|||j| 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   )rK   r\   r~   joinr   rM   )r0   request_tokenZpageZallow_permissionr   r   r   authorization_url  s    z1RequestTokenAuthorizationEngine.authorization_urlc             C   s6   | j |}| j|| |jdkr$dS |j|| 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)rO   make_end_user_authorize_tokenrA   r+   rf   )r0   r7   Zcredential_storeZrequest_token_stringr   r   r   __call__  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.
        )rM   rN   rP   )rO   rM   r	   rE   )r0   r7   Zauthorization_jsonr   r   r   rO   *  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.
        N)rh   )r0   r7   r   r   r   r   r   4  s    z=RequestTokenAuthorizationEngine.make_end_user_authorize_token)NNN)rT   rU   rV   rW   ZUNAUTHORIZED_ACCESS_LEVELr_   propertyrf   r   r   rO   r   r   r   r   r   r     s    
8
c                   sF   e Zd ZdZdZdZdZdZdZd fdd	Z	dd Z
dd Z  ZS )r   zThe simplest (and, right now, the only) request token authorizer.

    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.
    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...   z5Waiting to hear from Launchpad about your decision...www-browserlinkslinks2lynxelinkselinks-litenetrikw3mNc                s   t t| j||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   rb   r~   )r`   r   r   r_   Y  s    
z)AuthorizeRequestTokenWithBrowser.__init__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   outputn  s    z'AuthorizeRequestTokenWithBrowser.outputc             C   sj  | j |}| j| j|  y"tj }t|dd}|| jk}W n tjk
rZ   d}d}Y nX |r| j| j| j	  t
tgg g | j	\}}}|rtj  | j| j |dk	rtj| tj }	x|jdkrdtjt y|j| j P W n^ tk
rD }
 z@|
jjdkrt|
jn |
jjdkr$ntd t|
 W Y dd}
~
X nX tj |	t krtdt qW dS )z7Have the end-user authorize the token in their browser.basenameNFi  i  z#Unexpected response from Launchpad:zTimed out after %d seconds.)r   r   WAITING_FOR_USER
webbrowserr   getattrTERMINAL_BROWSERSErrorTIMEOUT_MESSAGETIMEOUTr   r   readlineWAITING_FOR_LAUNCHPADopentimerA   Zsleepaccess_token_poll_timerS   rM   r   r&   r#   EndUserDeclinedAuthorizationr'   r   access_token_poll_timeoutTokenAuthorizationTimedOut)r0   r7   r   r   Zbrowser_objZbrowserZconsole_browserZrlist_Z
start_timerg   r   r   r   r   w  sD    



z>AuthorizeRequestTokenWithBrowser.make_end_user_authorize_token)r   r   r   r   r   r   r   r   )NNN)rT   rU   rV   rW   r   r   r   r   r   r_   r   r   ra   r   r   )r`   r   r   E  s     	c               @   s   e Zd ZdS )TokenAuthorizationExceptionN)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )RequestTokenAlreadyAuthorizedN)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )r   N)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )r   N)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )ClientErrorN)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )ServerErrorN)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )NoLaunchpadAccountN)rT   rU   rV   r   r   r   r   r     s   r   c               @   s   e Zd ZdS )TooManyAuthenticationFailuresN)rT   rU   rV   r   r   r   r   r     s   r   i  )FZ
__future__r   typeZ__metaclass____all__r[   	cStringIOr
   ImportErrorior"   r   r   ry   sysr   r   Zurllib.parser   Zurllibr   Zurlparser   base64r   r   rG   Z
simplejsonrF   strZunicoder.   Zlazr.restfulclient.errorsr   Z"lazr.restfulclient.authorize.oauthr   Z_AccessTokenr   r   r   ro   r   rD   rR   rK   r   r   MemoryErrorKeyboardInterrupt
SystemExitrd   r   r(   r	   r   objectr   rk   ru   r   r   re   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   

v 	K= c