attached is our auth code.  i made a few minor adjustments to remove some 
things that are specific to our app - hopefully it still runs.

note that we use this as a decorator to controller methods.

On Saturday, April 26, 2014 6:09:13 PM UTC-7, samuel bonill wrote:
>
> Yes Christian, I'd like take a look...
>
>
> 2014-04-26 17:24 GMT-05:00 Christian Foster Howes:
>
>> i have an oauth implementation that i used on app engine.  i can try and 
>> clean it up a touch and share it if you would like.
>>
>> cfh
>>
>>
>> On Saturday, April 26, 2014 7:05:55 AM UTC-7, samuel bonill wrote:
>>>
>>> thanks Marks, i'm using phonegap(android, iOS) as my client and 
>>> angularjs consume the API Rest.
>>> x509 its grate but, work x509 on app engine ?,  or what do you think 
>>> about use Oauth 2.0 <http://oauth.net/2/> ?
>>>
>>>
>>> 2014-04-25 21:41 GMT-05:00 Samuel Marks:
>>>
>>>> Sure, take a look at x509 at http://web2py.com/books/
>>>> default/chapter/29/09/access-control
>>>>
>>>>
>>>> Samuel Marks
>>>> http://linkedin.com/in/samuelmarks
>>>>
>>>>
>>>> On Sat, Apr 26, 2014 at 12:33 PM, samuel bonill wrote:
>>>>
>>>>> is there an example of API Rest authentication based in private/public 
>>>>> key with web2py?? 
>>>>> i don't want use username and password tokens for each request
>>>>>  
>>>>> -- 
>>>>> Resources:
>>>>> - http://web2py.com
>>>>> - http://web2py.com/book (Documentation)
>>>>> - http://github.com/web2py/web2py (Source code)
>>>>> - https://code.google.com/p/web2py/issues/list (Report Issues)
>>>>> --- 
>>>>> You received this message because you are subscribed to the Google 
>>>>> Groups "web2py-users" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send 
>>>>> an email to web2py+unsubscr...@googlegroups.com.
>>>>>
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>
>>>>
>>>>  -- 
>>>> Resources:
>>>> - http://web2py.com
>>>> - http://web2py.com/book (Documentation)
>>>> - http://github.com/web2py/web2py (Source code)
>>>> - https://code.google.com/p/web2py/issues/list (Report Issues)
>>>> --- 
>>>> You received this message because you are subscribed to a topic in the 
>>>> Google Groups "web2py-users" group.
>>>> To unsubscribe from this topic, visit https://groups.google.com/d/
>>>> topic/web2py/lXfe0tpGi8U/unsubscribe.
>>>> To unsubscribe from this group and all its topics, send an email to 
>>>> web2py+unsubscr...@googlegroups.com.
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>
>>>  -- 
>> Resources:
>> - http://web2py.com
>> - http://web2py.com/book (Documentation)
>> - http://github.com/web2py/web2py (Source code)
>> - https://code.google.com/p/web2py/issues/list (Report Issues)
>> --- 
>> You received this message because you are subscribed to a topic in the 
>> Google Groups "web2py-users" group.
>> To unsubscribe from this topic, visit 
>> https://groups.google.com/d/topic/web2py/lXfe0tpGi8U/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to 
>> web2py+unsubscr...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to web2py+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
import logging
import json
# using oauth2 lib from https://github.com/simplegeo/python-oauth2
import oauth2 as oauth
import uuid
from functools import wraps

from gluon.http import HTTP
from gluon.globals import current
from google.appengine.api import memcache

# before conditional models we came up with our own way to do this
# you probably have a different implementation
from datamodel import models
# a utitlity for figuring out client versions etc from a user agent
# specific to our app
from apprequest import parse_user_agent

class Consumer(dict):
    pass


class XAuthServer(oauth.Server):
    timestamp_threshold = 86400 # In seconds, 1 day

    def generate_consumer_token(self):
        key = str(uuid.uuid4())
        secret = str(uuid.uuid4())
        return oauth.Token(key, secret)

    def generate_access_token(self):
        key = str(uuid.uuid4())
        secret = str(uuid.uuid4())
        return oauth.Token(key, secret)


class XAuthProvider(object):
    def __init__(self, *args, **kwargs):
        self._server = XAuthServer()
        self._server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())

    def get_client(self, request=None):
        """Return the client from the OAuth parameters."""
        if not isinstance(request, oauth.Request):
            raise ValueError('Request is not an oauth request.')
        client_key = request.get_parameter('oauth_consumer_key')
        if not client_key:
            raise Exception('Missing "oauth_consumer_key" parameter in ' \
                'OAuth "Authorization" header')

        client = models.client.get_client_by_oauth_key(client_key)
        if not client:
            raise Exception('Client "%s" not found.' % client_key)
        client = Consumer(client)
        client.secret = client['oauth_secret']

        return client

    def is_valid(self, request):
        """Returns a Client object if this is a valid OAuth request."""
        try:
            client = self.get_client(request)
            user_token = None
            if 'oauth_token' in request:
                # get_end_user_by_oauth_token is a utility method that wraps
                # the DB query.  it stores the result in memcache for
                # next lookup.
                user = models.end_user.get_end_user_by_oauth_token(
                    request['oauth_token'])
                if not user:
                    raise Exception('Invalid oauth token')
                user_token = get_user_token(user)
            params = self._server.verify_request(request, client, user_token)
        except Exception, e:
            raise e

        return params

    def authenticate_user(self, request, client):
        """Authenticates a user."""
        try:
            if 'oauth_token' not in request or not request['oauth_token']:
                raise Exception('Missing oauth token')
            # get_end_user_by_oauth_token is a utility method that wraps
            # the DB query.  it stores the result in memcache for
            # next lookup.
            user = models.end_user.get_end_user_by_oauth_token(
                request['oauth_token'])
            if not user:
                raise Exception('Invalid oauth token')
            params = self._server.verify_request(
                request, client, get_user_token(user))
        except Exception, e:
            raise e

        return user


def get_oauth_request(request):
    """Return an OAuth Request object for the current web2py request."""
    method = request.env.request_method

    if not request.env.http_authorization:
        raise Exception('Missing http authorization header.')

    postdata = None
    if method in ('POST', 'PUT') and request.env.content_type and \
       request.env.content_type.startswith('application/x-www-form-urlencoded'):
        # In case the request body has already been read, reset the
        # position to the beginning
        request.body.reset()
        postdata = request.body.read()

    host = request.env.http_host
    url = '%s://%s%s' % (request.env.wsgi_url_scheme, host,
                         request.env.web2py_original_uri)


    return oauth.Request.from_request(
        method,
        url,
        headers={'Authorization': request.env.http_authorization},
        query_string=postdata)


def get_user_token(user):
    """Get a user's oauth token."""
    if not user.oauth_key or not user.oauth_secret:
        return None
    return oauth.Token(user.oauth_key, user.oauth_secret)


def xauth_request(required=True, forget=True):
    def decorator(f):
        """
        Decorator to authenticate xauth token and inject user object into
        keyword arguments.
        """
        @wraps(f)
        def wrapper(*args, **kwargs):
            if forget:
                current.session.forget()
            # Get the OAuth headers from the request
            oauth_token = current.request.env.http_app_oauth_token
            oauth_secret = current.request.env.http_app_oauth_secret
            if oauth_token and oauth_secret:
                # Parse the client name, version and platform from the headers
                app_client, app_version, app_platform = parse_user_agent(
                    current.request.env.http_user_agent
                )
                if not app_client:
                    raise HTTP(
                        400,
                        json.dumps({
                            'error': {
                                'message': 'invalid user agent',
                                'code': 14532,
                            }
                        }),
                        **{'Content-Type': 'application/json'}
                    )
                # Set the app client in the request
                current.request.xauth_client = {'code': app_client}
                # Try to get the user from ID from memcache
                oauth_token_namespace = 'authorization_token_for_user_id'
                user_id = memcache.get(
                    oauth_token,
                    namespace=oauth_token_namespace,
                )
                # Get the user with a key query
                if user_id:
                    user = models.end_user[user_id]
                    if not user:
                        # The user stored in memcache was invalid and should be
                        # deleted
                        memcache.delete(
                            oauth_token,
                            namespace=oauth_token_namespace,
                        )
                        if required:
                            raise HTTP(
                                401,
                                json.dumps({
                                    'error': {
                                        'message': 'bad credentials',
                                        'code': 14533,
                                    }
                                }),
                                **{'Content-Type': 'application/json'}
                            )
                        else:
                            current.request.xauth_user = None
                            return f(*args, **kwargs)
                else:
                    # Query the user based on the OAuth token
                    user = models.end_user(
                        models.end_user.oauth_key==oauth_token).select().first()
                    if user:
                        # Save the user into memcache to speed up the future
                        # authenticated requests
                        memcache.set(
                            oauth_token,
                            user.id,
                            namespace=oauth_token_namespace,
                        )
                    else:
                        if required:
                            # The user does not exist
                            logging.info('NO USER')
                            raise HTTP(
                                401,
                                json.dumps({
                                    'error': {
                                        'message': 'bad credentials',
                                        'code': 14533,
                                    }
                                }),
                                **{'Content-Type': 'application/json'}
                            )
                        else:
                            current.request.xauth_user = None
                            return f(*args, **kwargs)
                # Check the user's token+secret
                if user.oauth_key != oauth_token or \
                   user.oauth_secret != oauth_secret:
                    if required:
                        raise HTTP(
                            401,
                            json.dumps({
                                'error': {
                                    'message': 'bad credentials',
                                    'code': 14533,
                                }
                            }),
                            **{'Content-Type': 'application/json'}
                        )
                    else:
                        current.request.xauth_user = None
                        return f(*args, **kwargs)
                current.request.xauth_user = user
            # Use the old web2py authentication method
            else:
                xauth_provider = XAuthProvider()
                try:
                    request = get_oauth_request(current.request)
                    # @TODO: check difference in oauth_timestamp from our clock
                    client = xauth_provider.get_client(request)
                    current.request.xauth_client = client
                    auth_user = xauth_provider.authenticate_user(
                        request, client)
                    current.request.xauth_user = auth_user
                except Exception, e:
                    if required:
                        logging.warn(e)
                        # @TODO: figure out a better way to clean the oauth
                        # error msg if there is a colon then it's user
                        # specific info after trim that for logging
                        msg = str(e).split(':')[0]
                        googleanalytics.log_error_event(msg, 401)
                        if msg.startswith('Expired timestamp'):
                            raise HTTP(400, 'Expired timestamp')
                        else:
                            raise HTTP(401, 'Bad Credentials')
            return f(*args, **kwargs)
        return wrapper
    return decorator

Reply via email to