Merge branch '577_denoise_video_transcoding'
[mediagoblin.git] / mediagoblin / plugins / oauth / views.py
CommitLineData
2f5926a6 1# -*- coding: utf-8 -*-
f46e2a4d
JW
2# GNU MediaGoblin -- federated, autonomous media hosting
3# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18import logging
19import json
20
f46e2a4d
JW
21from urllib import urlencode
22from uuid import uuid4
23from datetime import datetime
f46e2a4d 24
88a9662b 25from mediagoblin.tools.response import render_to_response, redirect
f46e2a4d
JW
26from mediagoblin.decorators import require_active_login
27from mediagoblin.messages import add_message, SUCCESS, ERROR
28from mediagoblin.tools.translate import pass_to_ugettext as _
88a9662b
JW
29from mediagoblin.plugins.oauth.models import OAuthCode, OAuthToken, \
30 OAuthClient, OAuthUserClient
31from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \
32 AuthorizationForm
33from mediagoblin.plugins.oauth.tools import require_client_auth
34from mediagoblin.plugins.api.tools import json_response
f46e2a4d
JW
35
36_log = logging.getLogger(__name__)
37
38
39@require_active_login
88a9662b
JW
40def register_client(request):
41 '''
42 Register an OAuth client
43 '''
111a609d 44 form = ClientRegistrationForm(request.form)
f46e2a4d 45
88a9662b
JW
46 if request.method == 'POST' and form.validate():
47 client = OAuthClient()
111a609d
JW
48 client.name = unicode(request.form['name'])
49 client.description = unicode(request.form['description'])
50 client.type = unicode(request.form['type'])
88a9662b 51 client.owner_id = request.user.id
111a609d 52 client.redirect_uri = unicode(request.form['redirect_uri'])
f46e2a4d 53
88a9662b
JW
54 client.generate_identifier()
55 client.generate_secret()
56
57 client.save()
58
59 add_message(request, SUCCESS, _('The client {0} has been registered!')\
60 .format(
61 client.name))
62
63 return redirect(request, 'mediagoblin.plugins.oauth.list_clients')
64
65 return render_to_response(
66 request,
67 'oauth/client/register.html',
68 {'form': form})
69
70
71@require_active_login
72def list_clients(request):
73 clients = request.db.OAuthClient.query.filter(
74 OAuthClient.owner_id == request.user.id).all()
75 return render_to_response(request, 'oauth/client/list.html',
76 {'clients': clients})
77
78
79@require_active_login
80def list_connections(request):
81 connections = OAuthUserClient.query.filter(
82 OAuthUserClient.user == request.user).all()
83 return render_to_response(request, 'oauth/client/connections.html',
84 {'connections': connections})
85
86
87@require_active_login
88def authorize_client(request):
111a609d 89 form = AuthorizationForm(request.form)
88a9662b
JW
90
91 client = OAuthClient.query.filter(OAuthClient.id ==
92 form.client_id.data).first()
93
94 if not client:
95 _log.error('''No such client id as received from client authorization
96 form.''')
bf3b9e78 97 return BadRequest()
88a9662b
JW
98
99 if form.validate():
100 relation = OAuthUserClient()
101 relation.user_id = request.user.id
102 relation.client_id = form.client_id.data
103 if form.allow.data:
104 relation.state = u'approved'
105 elif form.deny.data:
106 relation.state = u'rejected'
107 else:
bf3b9e78 108 return BadRequest
88a9662b
JW
109
110 relation.save()
111
bf3b9e78 112 return redirect(request, location=form.next.data)
88a9662b
JW
113
114 return render_to_response(
115 request,
116 'oauth/authorize.html',
117 {'form': form,
118 'client': client})
119
120
121@require_client_auth
122@require_active_login
123def authorize(request, client):
124 # TODO: Get rid of the JSON responses in this view, it's called by the
125 # user-agent, not the client.
126 user_client_relation = OAuthUserClient.query.filter(
127 (OAuthUserClient.user == request.user)
128 & (OAuthUserClient.client == client))
129
130 if user_client_relation.filter(OAuthUserClient.state ==
131 u'approved').count():
132 redirect_uri = None
133
134 if client.type == u'public':
135 if not client.redirect_uri:
136 return json_response({
137 'status': 400,
138 'errors':
139 [u'Public clients MUST have a redirect_uri pre-set']},
140 _disable_cors=True)
141
142 redirect_uri = client.redirect_uri
143
144 if client.type == u'confidential':
145 redirect_uri = request.GET.get('redirect_uri', client.redirect_uri)
146 if not redirect_uri:
147 return json_response({
148 'status': 400,
149 'errors': [u'Can not find a redirect_uri for client: {0}'\
150 .format(client.name)]}, _disable_cors=True)
f46e2a4d
JW
151
152 code = OAuthCode()
153 code.code = unicode(uuid4())
154 code.user = request.user
88a9662b 155 code.client = client
f46e2a4d
JW
156 code.save()
157
158 redirect_uri = ''.join([
88a9662b 159 redirect_uri,
f46e2a4d
JW
160 '?',
161 urlencode({'code': code.code})])
162
163 _log.debug('Redirecting to {0}'.format(redirect_uri))
164
bf3b9e78 165 return redirect(request, location=redirect_uri)
f46e2a4d
JW
166 else:
167 # Show prompt to allow client to access data
168 # - on accept: send the user agent back to the redirect_uri with the
169 # code parameter
170 # - on deny: send the user agent back to the redirect uri with error
171 # information
111a609d 172 form = AuthorizationForm(request.form)
88a9662b
JW
173 form.client_id.data = client.id
174 form.next.data = request.url
175 return render_to_response(
176 request,
177 'oauth/authorize.html',
178 {'form': form,
179 'client': client})
f46e2a4d
JW
180
181
182def access_token(request):
183 if request.GET.get('code'):
88a9662b
JW
184 code = OAuthCode.query.filter(OAuthCode.code ==
185 request.GET.get('code')).first()
f46e2a4d
JW
186
187 if code:
111a609d
JW
188 if code.client.type == u'confidential':
189 client_identifier = request.GET.get('client_id')
190
191 if not client_identifier:
192 return json_response({
193 'error': 'invalid_request',
194 'error_description':
195 'Missing client_id in request'})
196
197 client_secret = request.GET.get('client_secret')
198
199 if not client_secret:
200 return json_response({
201 'error': 'invalid_request',
202 'error_description':
203 'Missing client_secret in request'})
204
205 if not client_secret == code.client.secret or \
206 not client_identifier == code.client.identifier:
207 return json_response({
208 'error': 'invalid_client',
209 'error_description':
210 'The client_id or client_secret does not match the'
211 ' code'})
212
f46e2a4d
JW
213 token = OAuthToken()
214 token.token = unicode(uuid4())
215 token.user = code.user
88a9662b 216 token.client = code.client
f46e2a4d
JW
217 token.save()
218
2f5926a6
SS
219 # expire time of token in full seconds
220 # timedelta.total_seconds is python >= 2.7 or we would use that
221 td = token.expires - datetime.now()
222 exp_in = 86400*td.days + td.seconds # just ignore µsec
223
f46e2a4d
JW
224 access_token_data = {
225 'access_token': token.token,
88a9662b 226 'token_type': 'bearer',
2f5926a6 227 'expires_in': exp_in}
88a9662b 228 return json_response(access_token_data, _disable_cors=True)
111a609d
JW
229 else:
230 return json_response({
231 'error': 'invalid_request',
232 'error_description':
233 'Invalid code'})
234 else:
235 return json_response({
236 'error': 'invalid_request',
237 'error_descriptin':
238 'Missing `code` parameter in request'})