o
    ΔaZy                     @   s  d dl mZ 	 eZg dZzd dlmZ W n ey$   d dlmZ Y nw d dl	Z	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y\   d dlmZ Y nw zd dlmZ W n eyt   d dlmZ Y nw d dlZd d	lmZmZ d d
lmZ eeu re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.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$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@ZAG d/d0 d0e@ZBG d1d2 d2eBZCG d3d4 d4eBZDG d5d6 d6eDZEG d7d8 d8e@ZFG d9d: d:e@ZGG d;d< d<e@ZHG d=d> d>e@ZId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.
    %LP_DISABLE_SSL_CERTIFICATE_VALIDATIONF)boolosenvironget r   r   :/usr/lib/python3/dist-packages/launchpadlib/credentials.py$_ssl_certificate_validation_disabledV   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.
    )"disable_ssl_certificate_validationPOST)methodheadersbody   )r   httplib2Httprequestr   statusr   )urlr"   paramscert_disabledresponsecontentr   r   r   
_http_postc   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`
    N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)self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.
        r2   )r5   r6   decodeloadr
   )clsvaluecredentialsr   r   r   from_string   s
   

zCredentials.from_stringc           	      C   s   | j dus	J d| jdu sJ dt|}t| j jddd}|t }d|i}|| jkr1d|d	< t|||\}}t	|t
rC|d
}|| jkr]t|}|durU||d< t|| _|S t|| _d|t| jjf }|durz|| j_|d| 7 }|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/jsonAcceptr2   
lp.contextz%s%s?oauth_token=%sz&lp.context=%s)consumeraccess_tokenr   lookup_web_rootr0   keyrequest_token_pageDICT_TOKEN_FORMATr.   r5   bytesr<   jsonloadsr   from_params_request_tokenrA   authorize_token_pagecontext)	r8   rV   web_roottoken_formatr*   r)   r"   r,   r-   r   r   r   get_request_token   s>   





zCredentials.get_request_tokenc                 C   sl   | j dus	J 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.rB   z&%s)rD   rE   oauth_tokenrF   rG   )rT   r   rL   r0   rJ   rM   secretaccess_token_pager.   r   rA   rK   )r8   rW   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__rT   URI_TOKEN_FORMATrO   ITEM_SEPARATORNEWLINEr;   classmethodrA   r   STAGING_WEB_ROOTrY   r]   r   r   r   r   r	   r   s     

>r	   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.rZ   oauth_token_secretrI   )r   )r>   r*   rM   r[   rV   r   r   r   rS      s   
zAccessToken.from_paramsc                 C   s   t |ts
|d}t|dd}|d }t|dksJ d|d }|d }t|dks0J d	|d }|d
}|durKt|dksGJ d|d }| |||S )z<Create and return a new `AccessToken` from the given string.r2   F)keep_blank_valuesrZ   r   z/Query string must have exactly one oauth_token.r   rg   z*Query string must have exactly one secret.rI   Nz*Query string must have exactly one context)r5   r6   r<   r   lenr   )r>   query_stringr*   rM   r[   rV   r   r   r   rA      s"   



zAccessToken.from_stringN)r^   r_   r`   ra   re   rS   rA   r   r   r   r   r      s    
r   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__r8   	__class__r   r   rm     s   zAnonymousAccessToken.__init__)r^   r_   r`   ra   rm   __classcell__r   r   ro   r   r     s    r   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r8   rr   r   r   r   rm   &  s   
zCredentialStore.__init__c              
   C   s^   z	|  || W |S  ty     ty. } z| jdu r||   W Y d}~|S d}~ww )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	Exceptionrr   )r8   r@   unique_consumer_ider   r   r   r3   0  s   
zCredentialStore.savec                 C      t  )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.
        NotImplementedError)r8   r@   rw   r   r   r   rt   @  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r8   
unique_keyr   r   r   r=   I  s   
zCredentialStore.loadc                 C   ry   )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.
        rz   r}   r   r   r   r|   [  s   zCredentialStore.do_loadN)	r^   r_   r`   ra   rm   r3   rt   r=   r|   r   r   r   r   r     s    

	r   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 d S r   )rl   r   rm   	_fallbackMemoryCredentialStore)r8   rr   fallbackro   r   r   rm   s  s
   zKeyringCredentialStore.__init__c                   C   sL   dt  vr	ddladt  vr$z	ddlma W dS  ty#   taY dS w 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)r   )globalsr   keyring.errorsr   ImportErrorRuntimeErrorr   r   r   r   _ensure_keyring_importedy  s   
	

z/KeyringCredentialStore._ensure_keyring_importedc              
   C   s   |    | }| jt| }ztd||d W dS  tyF } zttkr/dt	|vr/ | j
r:| j
|| n W Y d}~dS d}~ww )z2Store newly-authorized credentials in the keyring.launchpadlibr2   $No recommended backend was availableN)r   r;   	B64MARKERr   r   set_passwordr<   r   r   strr   r3   )r8   r@   r~   r:   rx   r   r   r   rt     s&   

zKeyringCredentialStore.do_savec              
   C   s   |    ztd|}W n' ty3 } zttkrdt|vr | jr.| j|W  Y d}~S  d}~ww |durst|t	rB|
d}|| jr`zt|t| jd }W n
 ty_   Y dS w zt|}|W S  tyr   Y dS w dS )z&Retrieve credentials from the keyring.r   r   Nutf8)r   r   get_passwordr   r   r   r   r=   r5   r6   r7   
startswithr   r   ri   	TypeErrorr	   rA   rv   )r8   r~   credential_stringrx   r@   r   r   r   r|     sD   



zKeyringCredentialStore.do_load)NF)r^   r_   r`   ra   r   rm   staticmethodr   rt   r|   rq   r   r   ro   r   r   i  s    
r   c                       2   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 r   )rl   r   rm   filename)r8   r   rr   ro   r   r   rm     s   

z'UnencryptedFileCredentialStore.__init__c                 C   s   | | j dS )zSave the credentials to disk.N)save_to_pathr   r8   r@   r~   r   r   r   rt     s   z&UnencryptedFileCredentialStore.do_savec                 C   s4   t j| jrt | jtj dkst| jS dS )zLoad the credentials from disk.r   N)r   pathexistsr   statST_SIZEr	   load_from_pathr}   r   r   r   r|     s   z&UnencryptedFileCredentialStore.do_loadr   r^   r_   r`   ra   rm   rt   r|   rq   r   r   ro   r   r     s
    r   c                       r   )
r   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 r   )rl   r   rm   _credentialsrs   ro   r   r   rm     s   
zMemoryCredentialStore.__init__c                 C   s   || j |< dS )z!Store the credentials in our dictN)r   r   r   r   r   rt     s   zMemoryCredentialStore.do_savec                 C   s   | j |S )z&Retrieve the credentials from our dict)r   r   r}   r   r   r   r|     s   zMemoryCredentialStore.do_loadr   r   r   r   ro   r   r     s
    r   c                   @   sP   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`.
    UNAUTHORIZEDNc                 C   s   t || _t || _|du r|du rtd|dur(|dur(td||f |du r4dg}t|}nt|}|}|| _|| _	|pCg | _
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.)DESKTOP_INTEGRATION)r   lookup_service_rootservice_rootweb_root_for_service_rootrW   
ValueErrorr   r   rJ   application_nameallow_access_levels)r8   r   r   consumer_namer   rJ   r   r   r   rm   	  s(    
z(RequestTokenAuthorizationEngine.__init__c                 C   s   | j jd | j S )z7Return a string identifying this consumer on this host.@)rJ   rM   r   rn   r   r   r   rw   I  s   z2RequestTokenAuthorizationEngine.unique_consumer_idc                 C   s>   dt |f }d}t| jdkr|||| 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   )rU   ri   r   joinr   rW   )r8   request_tokenpageallow_permissionr   r   r   authorization_urlN  s   z1RequestTokenAuthorizationEngine.authorization_urlc                 C   s6   |  |}| || |jdu 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)rY   make_end_user_authorize_tokenrK   r3   rw   )r8   r@   credential_store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.
        )rW   rX   rZ   )rY   rW   r	   rO   )r8   r@   authorization_jsonr   r   r   rY   {  s   z1RequestTokenAuthorizationEngine.get_request_tokenc                 C   ry   )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.
        rz   )r8   r@   r   r   r   r   r     s   z=RequestTokenAuthorizationEngine.make_end_user_authorize_tokenNNN)r^   r_   r`   ra   UNAUTHORIZED_ACCESS_LEVELrm   propertyrw   r   r   rY   r   r   r   r   r   r     s    
@

r   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)r8   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)r8   r   r   r   r   !notify_end_user_authorization_url  s   z>AuthorizeRequestTokenWithURL.notify_end_user_authorization_urlc              
   C   sp   z| | j W n* ty2 } z|jjdkrt|j|jjdkr)td t| t|jd}~ww |j	duS )z Check if the end-user authorizedi  i  z#Unexpected response from Launchpad:N)
r]   rW   r   r,   r(   EndUserDeclinedAuthorizationr-   r   EndUserNoAuthorizationrK   )r8   r@   rx   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   )r8   r@   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   )r8   r@   r   r   r   r   r   r     s   

z:AuthorizeRequestTokenWithURL.make_end_user_authorize_tokenN)r^   r_   r`   ra   r   r   r   r   r   r   r   r   r   r   r   r     s    	r   c                       sP   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-browserlinkslinks2lynxelinkszelinks-litenetrik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)rl   r   rm   )r8   r   r   r   rr   r   ro   r   r   rm     s   
z)AuthorizeRequestTokenWithBrowser.__init__c                    s   t t| | zt }t|dd}|| jv }W n tjy'   d}d}Y nw |rE| | j	| j
  ttgg g | j
\}}}|rEt  |durPt| dS dS )r   basenameNF)rl   r   r   
webbrowserr   getattrTERMINAL_BROWSERSErrorr   TIMEOUT_MESSAGETIMEOUTr   r   r   open)r8   r   browser_objbrowserconsole_browserrlist_ro   r   r   r     s(   zBAuthorizeRequestTokenWithBrowser.notify_end_user_authorization_urlc                 C   s~   |  | j t }|jdu r=tt z
| |rW dS W n	 ty'   Y nw t |t kr6t	dt |jdu sdS dS )r   NzTimed out after %d seconds.)
r   r   timerK   sleepaccess_token_poll_timer   r   access_token_poll_timeoutTokenAuthorizationTimedOut)r8   r@   
start_timer   r   r   r   &  s    


z@AuthorizeRequestTokenWithBrowser.wait_for_end_user_authorizationr   )r^   r_   r`   ra   r   r   r   r   r   rm   r   r   rq   r   r   ro   r   r     s    r   c                   @      e Zd ZdS )TokenAuthorizationExceptionNr^   r_   r`   r   r   r   r   r   7      r   c                   @   r   )RequestTokenAlreadyAuthorizedNr   r   r   r   r   r   ;  r   r   c                   @      e Zd ZdZdS )EndUserAuthorizationFailedz?Superclass exception for all failures of end-user authorizationNr^   r_   r`   ra   r   r   r   r   r   ?      r   c                   @   r   )r   zEnd-user declined authorizationNr   r   r   r   r   r   E  r   r   c                   @   r   )r   z*End-user did not perform any authorizationNr   r   r   r   r   r   K  r   r   c                   @   r   )r   z<End-user did not perform any authorization in timeout periodNr   r   r   r   r   r   Q  r   r   c                   @   r   )ClientErrorNr   r   r   r   r   r   W  r   r   c                   @   r   )ServerErrorNr   r   r   r   r   r   [  r   r   c                   @   r   )NoLaunchpadAccountNr   r   r   r   r   r   _  r   r   c                   @   r   )TooManyAuthenticationFailuresNr   r   r   r   r   r   c  r   r   )J
__future__r   type__metaclass____all__	cStringIOr
   r   ior%   rQ   r   r   r   sysr   r   urllib.parser   urllibr   urlparser   base64r   r   six.moves.urllib.parser   rP   r   unicoder6   lazr.restfulclient.errorsr   "lazr.restfulclient.authorize.oauthr   _AccessTokenr   r   r   r   r   rN   r\   rU   r   r   MemoryErrorKeyboardInterrupt
SystemExitru   r   r.   r	   r   objectr   r   r   r   r   r   r   rv   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   

  
Kf ;f