# GNU MediaGoblin -- federated, autonomous media hosting # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import logging import json from webob import exc, Response from urllib import urlencode from uuid import uuid4 from datetime import datetime 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.tools.translate import pass_to_ugettext as _ from mediagoblin.plugins.oauth.models import OAuthCode, OAuthToken, \ OAuthClient, OAuthUserClient from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \ AuthorizationForm from mediagoblin.plugins.oauth.tools import require_client_auth from mediagoblin.plugins.api.tools import json_response _log = logging.getLogger(__name__) @require_active_login def register_client(request): ''' Register an OAuth client ''' form = ClientRegistrationForm(request.form) 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.owner_id = request.user.id client.redirect_uri = unicode(request.form['redirect_uri']) client.generate_identifier() client.generate_secret() client.save() add_message(request, SUCCESS, _('The client {0} has been registered!')\ .format( client.name)) return redirect(request, 'mediagoblin.plugins.oauth.list_clients') return render_to_response( request, 'oauth/client/register.html', {'form': form}) @require_active_login def list_clients(request): clients = request.db.OAuthClient.query.filter( OAuthClient.owner_id == request.user.id).all() return render_to_response(request, 'oauth/client/list.html', {'clients': clients}) @require_active_login def list_connections(request): connections = OAuthUserClient.query.filter( OAuthUserClient.user == request.user).all() return render_to_response(request, 'oauth/client/connections.html', {'connections': connections}) @require_active_login def authorize_client(request): form = AuthorizationForm(request.form) client = OAuthClient.query.filter(OAuthClient.id == form.client_id.data).first() if not client: _log.error('''No such client id as received from client authorization form.''') return exc.HTTPBadRequest() if form.validate(): relation = OAuthUserClient() relation.user_id = request.user.id relation.client_id = form.client_id.data if form.allow.data: relation.state = u'approved' elif form.deny.data: relation.state = u'rejected' else: return exc.HTTPBadRequest relation.save() return exc.HTTPFound(location=form.next.data) return render_to_response( request, 'oauth/authorize.html', {'form': form, 'client': client}) @require_client_auth @require_active_login def authorize(request, client): # TODO: Get rid of the JSON responses in this view, it's called by the # user-agent, not the client. user_client_relation = OAuthUserClient.query.filter( (OAuthUserClient.user == request.user) & (OAuthUserClient.client == client)) if user_client_relation.filter(OAuthUserClient.state == u'approved').count(): redirect_uri = None if client.type == u'public': if not client.redirect_uri: return json_response({ 'status': 400, 'errors': [u'Public clients MUST have a redirect_uri pre-set']}, _disable_cors=True) redirect_uri = client.redirect_uri if client.type == u'confidential': redirect_uri = request.GET.get('redirect_uri', client.redirect_uri) 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) code = OAuthCode() code.code = unicode(uuid4()) code.user = request.user code.client = client code.save() redirect_uri = ''.join([ redirect_uri, '?', urlencode({'code': code.code})]) _log.debug('Redirecting to {0}'.format(redirect_uri)) return exc.HTTPFound(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 # code parameter # - on deny: send the user agent back to the redirect uri with error # information form = AuthorizationForm(request.form) form.client_id.data = client.id form.next.data = request.url return render_to_response( request, 'oauth/authorize.html', {'form': form, 'client': client}) def access_token(request): if request.GET.get('code'): 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() access_token_data = { 'access_token': token.token, 'token_type': 'bearer', 'expires_in': int( round( (token.expires - datetime.now()).total_seconds()))} return json_response(access_token_data, _disable_cors=True) else: return json_response({ 'error': 'invalid_request', 'error_description': 'Invalid code'}) else: return json_response({ 'error': 'invalid_request', 'error_descriptin': 'Missing `code` parameter in request'})