From 2f5926a65d4e56d4ab9c7bfd6b3de25a032b8be5 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 14 Dec 2012 10:54:53 +0100 Subject: Fiy python2.7'ism (#566) The oauth plugin used timedelta.total_seconds which was introduced in python 2.7 only. To preserve backwards compatability, we simply calculate the time difference in seconds manually. I considered monkeypatching total_seconds to the timedelta object, but it is a built-in type written in C (I believe) and modifying attributes failed horribly. Switch this to use total_seconds once we require python 2.7 as minimum version. Signed-off-by: Sebastian Spaeth --- mediagoblin/plugins/oauth/views.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'mediagoblin/plugins/oauth/views.py') diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py index cf605fd2..643c2783 100644 --- a/mediagoblin/plugins/oauth/views.py +++ b/mediagoblin/plugins/oauth/views.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # GNU MediaGoblin -- federated, autonomous media hosting # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. # @@ -216,12 +217,15 @@ def access_token(request): token.client = code.client token.save() + # expire time of token in full seconds + # timedelta.total_seconds is python >= 2.7 or we would use that + td = token.expires - datetime.now() + exp_in = 86400*td.days + td.seconds # just ignore µsec + access_token_data = { 'access_token': token.token, 'token_type': 'bearer', - 'expires_in': int( - round( - (token.expires - datetime.now()).total_seconds()))} + 'expires_in': exp_in} return json_response(access_token_data, _disable_cors=True) else: return json_response({ -- cgit v1.2.3 From bf3b9e783dc86b38864e66c2aab149472e636461 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 16 Nov 2012 11:13:01 +0100 Subject: Transition webob BadRequest|HTTPFound to webob/redirect More transitioning away from webob Response import from webob was unused --- mediagoblin/plugins/oauth/views.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'mediagoblin/plugins/oauth/views.py') diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py index 643c2783..c7b2a332 100644 --- a/mediagoblin/plugins/oauth/views.py +++ b/mediagoblin/plugins/oauth/views.py @@ -18,7 +18,6 @@ import logging import json -from webob import exc, Response from urllib import urlencode from uuid import uuid4 from datetime import datetime @@ -95,7 +94,7 @@ def authorize_client(request): if not client: _log.error('''No such client id as received from client authorization form.''') - return exc.HTTPBadRequest() + return BadRequest() if form.validate(): relation = OAuthUserClient() @@ -106,11 +105,11 @@ def authorize_client(request): elif form.deny.data: relation.state = u'rejected' else: - return exc.HTTPBadRequest + return BadRequest relation.save() - return exc.HTTPFound(location=form.next.data) + return redirect(request, location=form.next.data) return render_to_response( request, @@ -163,7 +162,7 @@ def authorize(request, client): _log.debug('Redirecting to {0}'.format(redirect_uri)) - return exc.HTTPFound(location=redirect_uri) + return redirect(request, location=redirect_uri) else: # Show prompt to allow client to access data # - on accept: send the user agent back to the redirect_uri with the -- cgit v1.2.3 From 29f523e1db243e0b2d4726d93f2d64bc67f84e4d Mon Sep 17 00:00:00 2001 From: Hans Lo Date: Thu, 28 Mar 2013 00:08:18 -0400 Subject: Use WTForms data field in plugins/oauth/views.py --- mediagoblin/plugins/oauth/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'mediagoblin/plugins/oauth/views.py') diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py index c7b2a332..ea45c209 100644 --- a/mediagoblin/plugins/oauth/views.py +++ b/mediagoblin/plugins/oauth/views.py @@ -45,11 +45,11 @@ def register_client(request): if request.method == 'POST' and form.validate(): client = OAuthClient() - client.name = unicode(request.form['name']) - client.description = unicode(request.form['description']) - client.type = unicode(request.form['type']) + client.name = unicode(form.name.data) + client.description = unicode(form.description.data) + client.type = unicode(form.type.data) client.owner_id = request.user.id - client.redirect_uri = unicode(request.form['redirect_uri']) + client.redirect_uri = unicode(form.redirect_uri.data) client.generate_identifier() client.generate_secret() -- cgit v1.2.3 From c121a7d3d0c48d4e3abf43c06047dde3e25616c3 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Sun, 10 Mar 2013 22:52:07 +0100 Subject: OAuth: Support refresh tokens, etc Initially I was going to write a failing test for refresh tokens. Thus this fix includes an orphaned 'expect_failure' method in test utils. I ended up writing support for OAuth refresh tokens, as well as a lot of cleanup (hopefully) in the OAuth plugin code. **Rebase**: While waiting for this stuff to be merged, the testing framework changed, it comes with batteries included regarding fails. Removed legacy nosetest helper. Also added a lot of backref=backref([...], cascade='all, delete-orphan') --- mediagoblin/plugins/oauth/views.py | 150 ++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 67 deletions(-) (limited to 'mediagoblin/plugins/oauth/views.py') diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py index ea45c209..d6fd314f 100644 --- a/mediagoblin/plugins/oauth/views.py +++ b/mediagoblin/plugins/oauth/views.py @@ -16,21 +16,21 @@ # along with this program. If not, see . import logging -import json from urllib import urlencode -from uuid import uuid4 -from datetime import datetime + +from werkzeug.exceptions import BadRequest from mediagoblin.tools.response import render_to_response, redirect from mediagoblin.decorators import require_active_login -from mediagoblin.messages import add_message, SUCCESS, ERROR +from mediagoblin.messages import add_message, SUCCESS from mediagoblin.tools.translate import pass_to_ugettext as _ -from mediagoblin.plugins.oauth.models import OAuthCode, OAuthToken, \ - OAuthClient, OAuthUserClient +from mediagoblin.plugins.oauth.models import OAuthCode, OAuthClient, \ + OAuthUserClient, OAuthRefreshToken from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \ AuthorizationForm -from mediagoblin.plugins.oauth.tools import require_client_auth +from mediagoblin.plugins.oauth.tools import require_client_auth, \ + create_token from mediagoblin.plugins.api.tools import json_response _log = logging.getLogger(__name__) @@ -51,9 +51,6 @@ def register_client(request): client.owner_id = request.user.id client.redirect_uri = unicode(form.redirect_uri.data) - client.generate_identifier() - client.generate_secret() - client.save() add_message(request, SUCCESS, _('The client {0} has been registered!')\ @@ -92,9 +89,9 @@ def authorize_client(request): form.client_id.data).first() if not client: - _log.error('''No such client id as received from client authorization - form.''') - return BadRequest() + _log.error('No such client id as received from client authorization \ +form.') + raise BadRequest() if form.validate(): relation = OAuthUserClient() @@ -105,7 +102,7 @@ def authorize_client(request): elif form.deny.data: relation.state = u'rejected' else: - return BadRequest + raise BadRequest() relation.save() @@ -136,7 +133,7 @@ def authorize(request, client): return json_response({ 'status': 400, 'errors': - [u'Public clients MUST have a redirect_uri pre-set']}, + [u'Public clients should have a redirect_uri pre-set.']}, _disable_cors=True) redirect_uri = client.redirect_uri @@ -146,11 +143,10 @@ def authorize(request, client): if not redirect_uri: return json_response({ 'status': 400, - 'errors': [u'Can not find a redirect_uri for client: {0}'\ - .format(client.name)]}, _disable_cors=True) + 'errors': [u'No redirect_uri supplied!']}, + _disable_cors=True) code = OAuthCode() - code.code = unicode(uuid4()) code.user = request.user code.client = client code.save() @@ -180,59 +176,79 @@ def authorize(request, client): def access_token(request): + ''' + Access token endpoint provides access tokens to any clients that have the + right grants/credentials + ''' + + client = None + user = None + if request.GET.get('code'): + # Validate the code arg, then get the client object from the db. code = OAuthCode.query.filter(OAuthCode.code == request.GET.get('code')).first() - if code: - if code.client.type == u'confidential': - client_identifier = request.GET.get('client_id') - - if not client_identifier: - return json_response({ - 'error': 'invalid_request', - 'error_description': - 'Missing client_id in request'}) - - client_secret = request.GET.get('client_secret') - - if not client_secret: - return json_response({ - 'error': 'invalid_request', - 'error_description': - 'Missing client_secret in request'}) - - if not client_secret == code.client.secret or \ - not client_identifier == code.client.identifier: - return json_response({ - 'error': 'invalid_client', - 'error_description': - 'The client_id or client_secret does not match the' - ' code'}) - - token = OAuthToken() - token.token = unicode(uuid4()) - token.user = code.user - token.client = code.client - token.save() - - # expire time of token in full seconds - # timedelta.total_seconds is python >= 2.7 or we would use that - td = token.expires - datetime.now() - exp_in = 86400*td.days + td.seconds # just ignore µsec - - access_token_data = { - 'access_token': token.token, - 'token_type': 'bearer', - 'expires_in': exp_in} - return json_response(access_token_data, _disable_cors=True) - else: + if not code: return json_response({ 'error': 'invalid_request', 'error_description': - 'Invalid code'}) - else: - return json_response({ - 'error': 'invalid_request', - 'error_descriptin': - 'Missing `code` parameter in request'}) + 'Invalid code.'}) + + client = code.client + user = code.user + + elif request.args.get('refresh_token'): + # Validate a refresh token, then get the client object from the db. + refresh_token = OAuthRefreshToken.query.filter( + OAuthRefreshToken.token == + request.args.get('refresh_token')).first() + + if not refresh_token: + return json_response({ + 'error': 'invalid_request', + 'error_description': + 'Invalid refresh token.'}) + + client = refresh_token.client + user = refresh_token.user + + if client: + client_identifier = request.GET.get('client_id') + + if not client_identifier: + return json_response({ + 'error': 'invalid_request', + 'error_description': + 'Missing client_id in request.'}) + + if not client_identifier == client.identifier: + return json_response({ + 'error': 'invalid_client', + 'error_description': + 'Mismatching client credentials.'}) + + if client.type == u'confidential': + client_secret = request.GET.get('client_secret') + + if not client_secret: + return json_response({ + 'error': 'invalid_request', + 'error_description': + 'Missing client_secret in request.'}) + + if not client_secret == client.secret: + return json_response({ + 'error': 'invalid_client', + 'error_description': + 'Mismatching client credentials.'}) + + + access_token_data = create_token(client, user) + + return json_response(access_token_data, _disable_cors=True) + + return json_response({ + 'error': 'invalid_request', + 'error_description': + 'Missing `code` or `refresh_token` parameter in request.'}) -- cgit v1.2.3