Exemplo n.º 1
0
    def access_user_id_and_email(self, oauth_map):

        token = OAuthToken(oauth_map.google_access_token,
                           oauth_map.google_access_token_secret)

        oauth_request = OAuthRequest.from_consumer_and_token(
                GoogleOAuthClient.Consumer,
                token=token,
                http_url="%sapi/auth/current_google_oauth_user_id_and_email" %
                    request.host_url
                )

        oauth_request.sign_request(OAuthSignatureMethod_HMAC_SHA1(),
                                   GoogleOAuthClient.Consumer, token)

        response = get_response(oauth_request.to_url())

        try:
            obj = json.loads(response)

            if len(obj) == 2:
                # (user_id, email)
                return tuple(obj)
        except ValueError:
            logging.error("Error decoding json from "
                          "current_google_oauth_user_id_and_email; "
                          "json was %r", response)
            pass

        return (None, None)
Exemplo n.º 2
0
    def fetch_request_token(self, oauth_map):
        oauth_request = OAuthRequest.from_consumer_and_token(
                GoogleOAuthClient.Consumer,
                http_url = "%s/_ah/OAuthGetRequestToken" % App.realm,
                callback = "%sapi/auth/google_token_callback?oauth_map_id=%s" % (request.host_url, oauth_map.key().id())
                )

        oauth_request.sign_request(OAuthSignatureMethod_HMAC_SHA1(), GoogleOAuthClient.Consumer, None)

        response = get_response(oauth_request.to_url())

        return OAuthToken.from_string(response)
Exemplo n.º 3
0
    def fetch_access_token(self, oauth_map):

        token = OAuthToken(oauth_map.google_request_token, oauth_map.google_request_token_secret)

        oauth_request = OAuthRequest.from_consumer_and_token(
                GoogleOAuthClient.Consumer,
                token = token,
                verifier = oauth_map.google_verification_code,
                http_url = "%s/_ah/OAuthGetAccessToken" % App.realm
                )

        oauth_request.sign_request(OAuthSignatureMethod_HMAC_SHA1(), GoogleOAuthClient.Consumer, token)

        response = get_response(oauth_request.to_url())

        return OAuthToken.from_string(response)
Exemplo n.º 4
0
def test_oauth1_0():
    """
    Test to veriy OAuth 1.0 flow
    """
    """
    After Jane informs printer.example.com that she would like to print her 
    vacation photo stored at photos.example.net, the printer website tries to 
    access the photo and receives HTTP 401 Unauthorized indicating it is private. 
    The Service Provider includes the following header with the response::
    """

    #TODO - not able to handle /request_token/ - returns 404
    #TODO - add test for reuse of nonce for the request token stage?
    #TODO - add test for oauth params in Authorization, GET and POST
    response = app.get('/request_token', status=401)
    assert response.status == '401 Invalid request parameters.'
    assert response.headers[
        'WWW-Authenticate'] == 'OAuth realm="http://events.example.net/"'
    assert response.body == 'Invalid request parameters.'
    """
    The Consumer sends the following HTTP POST request to the Service Provider::
    """
    import time
    parameters = {
        'oauth_consumer_key': CONSUMER_KEY,
        'oauth_signature_method': 'PLAINTEXT',
        'oauth_signature': '%s&' % CONSUMER_SECRET,
        'oauth_timestamp': str(int(time.time())),
        'oauth_nonce': 'requestnonce',
        'oauth_version': '1.0',
        #'scope': 'default', # custom argument to specify Protected Resource
    }

    response = app.post('/request_token', parameters)
    """
    The Service Provider checks the signature and replies with an unauthorized 
    Request Token in the body of the HTTP response::
    """
    assert response.status == '200 OK'

    tokens = Token.all().fetch(1000)
    #double checking the sanity of the test - there should only be one token in
    #the store at this stage

    assert len(tokens) == 1
    token = tokens[0]
    assert 'oauth_token_secret=%s&oauth_token=%s' % (
        token.secret, token.key_) == response.body

    #Ensure that the token returned is unauthorized request token
    assert token.is_approved == False
    assert token.token_type == Token.REQUEST
    """
    If you try to access a resource with a wrong scope, it will return an error::
    """

    #the scope related tests are not here as they the scope
    #function has not been implemented yet
    #>> parameters['scope'] = 'videos'
    #>>> response = c.get("/oauth/request_token/", parameters)
    #>>> response.status_code
    #401
    #>>> response.content
    #'Resource videos does not exist.'
    """
    Requesting User Authorization
    -----------------------------
    
    The Consumer redirects Jane's browser to the Service Provider User 
    Authorization URL to obtain Jane's approval for accessing her private photos.
    
    The Service Provider asks Jane to sign-in using her username and password::
    
    """
    parameters = {
        'oauth_token': token.key_,
        'oauth_callback': 'http://test.com/request_token_ready',
        'authorize_access': 1,
    }

    response = app.post('/authorize', parameters)
    assert response.status == '302 Moved Temporarily'
    assert response.location ==\
'http://localhost/_ah/login?continue=http%%3A//localhost/authorize%%3Foauth_token%%3D%s%%26oauth_callback%%3Dhttp%%3A//test.com/request_token_ready'%(token.key_)

    #the token details should not be altered by this call
    tokens = Token.all().fetch(1000)
    assert len(tokens) == 1
    token = tokens[0]

    assert token.is_approved == False
    assert token.token_type == Token.REQUEST

    #In reality the user would of been redirected to the Google login page and
    #then redirected to a page where they will be given an opportunity to
    #authorize the application or otherwise.

    #Here we simulate the user being logged (by setting the
    #os.environ['USER_EMAIL']) variable and the user giving authorizing by
    #setting the 'authorize_access' parameter
    #and redirect manually to the authorization page
    os.environ['USER_EMAIL'] = '*****@*****.**'
    response = app.post('/authorize', parameters)

    #verify that the request token is now approved
    tokens = Token.all().fetch(1000)
    assert len(tokens) == 1
    token = tokens[0]

    assert token.is_approved == True
    assert token.token_type == Token.REQUEST

    #verify the response - now that we are approved we should get
    assert response.status == '302 Moved Temporarily'
    assert response.location == 'http://test.com/request_token_ready?oauth_token=%s' % (
        token.key_)

    #TODO add test for case where the user rejects the approval,
    #now the flow assumes that it will always be approved upon logging in
    """
    Obtaining an Access Token
    -------------------------
    
    Now that the Consumer knows Jane approved the Request Token, it asks the 
    Service Provider to exchange it for an Access Token::
    """

    parameters = {
        'oauth_consumer_key': CONSUMER_KEY,
        'oauth_token': token.key_,
        'oauth_signature_method': 'PLAINTEXT',
        'oauth_signature': '%s&%s' % (CONSUMER_SECRET, token.secret),
        'oauth_timestamp': str(int(time.time())),
        'oauth_nonce': 'accessnonce',
        'oauth_version': '1.0',
    }
    response = app.post("/access_token", parameters)
    """
    The Service Provider checks the signature and replies with an Access Token in 
    the body of the HTTP response::
    """

    response.status == '200 OK'
    #verify the access token now

    access_tokens = Token.all()\
        .filter('token_type =',Token.ACCESS).fetch(1000)
    #double checking the sanity of the test - there should only be one token in
    #the store at this stage

    assert len(access_tokens) == 1
    access_token = access_tokens[0]
    assert 'oauth_token_secret=%s&oauth_token=%s' % (
        access_token.secret, access_token.key_) == response.body

    assert str(access_token.user) == '*****@*****.**'
    """
    The Consumer will not be able to request another Access Token with the same
    Nonce::
    """

    nonces = Nonce.all().fetch(1000)
    assert nonces[0].key_ == "accessnonce"

    response = app.post("/access_token", parameters, status=401)

    assert response.status == '401 Nonce already used: accessnonce'
    assert response.body == 'Nonce already used: accessnonce'
    """
    The Consumer will not be able to request an Access Token if the token is not
    approved::
    """

    token.is_approved = False
    token.put()

    parameters['oauth_nonce'] = 'anotheraccessnonce'
    response = app.post("/access_token", parameters, status=401)
    response.status == "401 Consumer key or token key does not match. Make sure your request token is approved. Check your verifier too if you use OAuth 1.0a."
    response.body == 'Consumer key or token key does not match. Make sure your request token is approved. Check your verifier too if you use OAuth 1.0a.'
    """
    Accessing Protected Resources
    -----------------------------
    
    The Consumer is now ready to request the private photo. Since the photo URL is 
    not secure (HTTP), it must use HMAC-SHA1.
    
    Generating Signature Base String
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    To generate the signature, it first needs to generate the Signature Base 
    String. The request contains the following parameters (oauth_signature 
    excluded) which are ordered and concatenated into a normalized string::
    """

    parameters = {
        'oauth_consumer_key': CONSUMER_KEY,
        'oauth_token': access_token.key_,
        'oauth_signature_method': 'HMAC-SHA1',
        'oauth_timestamp': str(int(time.time())),
        'oauth_nonce': 'accessresourcenonce',
        'oauth_version': '1.0',
    }
    """
    Calculating Signature Value
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    HMAC-SHA1 produces the following digest value as a base64-encoded string 
    (using the Signature Base String as text and kd94hf93k423kf44&pfkkdhi9sl3r4s00 
    as key)::
    """

    consumer = Consumer.all().fetch(1000)[0]

    #we dont use the OAuthRequest.from_token_and_callback function as how the
    #original method does as it does a token.key instead of token.key_

    oauth_request = OAuthRequest('POST', 'http://localhost/protected',
                                 parameters)
    signature_method = OAuthSignatureMethod_HMAC_SHA1()
    signature = signature_method.build_signature(oauth_request, consumer,
                                                 access_token)
    """
    Requesting Protected Resource
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    All together, the Consumer request for the photo is::
    """

    parameters['oauth_signature'] = signature
    response = app.post("/protected", parameters)

    assert response.status == '200 OK'
    assert response.body == 'Protected Resource access!'
    """
    Otherwise, an explicit error will be raised::
    """

    parameters['oauth_signature'] = 'wrongsignature'
    parameters['oauth_nonce'] = 'anotheraccessresourcenonce'
    response = app.post("/protected", parameters, status=401)

    assert '401 Invalid signature. Expected signature base string: POST' in response.status
    assert 'Invalid signature. Expected signature base string: POST' in response.body

    response = app.post("/protected", status=401)

    #TODO - why are we getting "Invalid OAuth parameters instead of Invalid request parameters"
    assert response.status == '401 Invalid OAuth parameters'
    assert response.headers[
        'WWW-Authenticate'] == 'OAuth realm="http://events.example.net/"'
    assert response.body == 'Invalid OAuth parameters'
    """
    Revoking Access
    ---------------
    
    If Jane deletes the Access Token of printer.example.com, the Consumer will not 
    be able to access the Protected Resource anymore::
    """
    access_token_key = access_token.key_
    access_token.delete()
    parameters['oauth_nonce'] = 'yetanotheraccessresourcenonce'

    oauth_request = OAuthRequest('POST', 'http://localhost/protected',
                                 parameters)
    signature_method = OAuthSignatureMethod_HMAC_SHA1()
    signature = signature_method.build_signature(oauth_request, consumer,
                                                 access_token)

    parameters['oauth_signature'] = signature

    response = app.post("/protected", parameters, status=401)

    assert response.status == '401 Invalid access token: %s' % (
        access_token_key)
    assert response.body == 'Invalid access token: %s' % (access_token_key)
    """