Merge branch '577_denoise_video_transcoding'
[mediagoblin.git] / mediagoblin / plugins / oauth / views.py
1 # -*- coding: utf-8 -*-
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
18 import logging
19 import json
20
21 from urllib import urlencode
22 from uuid import uuid4
23 from datetime import datetime
24
25 from mediagoblin.tools.response import render_to_response, redirect
26 from mediagoblin.decorators import require_active_login
27 from mediagoblin.messages import add_message, SUCCESS, ERROR
28 from mediagoblin.tools.translate import pass_to_ugettext as _
29 from mediagoblin.plugins.oauth.models import OAuthCode, OAuthToken, \
30 OAuthClient, OAuthUserClient
31 from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \
32 AuthorizationForm
33 from mediagoblin.plugins.oauth.tools import require_client_auth
34 from mediagoblin.plugins.api.tools import json_response
35
36 _log = logging.getLogger(__name__)
37
38
39 @require_active_login
40 def register_client(request):
41 '''
42 Register an OAuth client
43 '''
44 form = ClientRegistrationForm(request.form)
45
46 if request.method == 'POST' and form.validate():
47 client = OAuthClient()
48 client.name = unicode(request.form['name'])
49 client.description = unicode(request.form['description'])
50 client.type = unicode(request.form['type'])
51 client.owner_id = request.user.id
52 client.redirect_uri = unicode(request.form['redirect_uri'])
53
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
72 def 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
80 def 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
88 def authorize_client(request):
89 form = AuthorizationForm(request.form)
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.''')
97 return BadRequest()
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:
108 return BadRequest
109
110 relation.save()
111
112 return redirect(request, location=form.next.data)
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
123 def 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)
151
152 code = OAuthCode()
153 code.code = unicode(uuid4())
154 code.user = request.user
155 code.client = client
156 code.save()
157
158 redirect_uri = ''.join([
159 redirect_uri,
160 '?',
161 urlencode({'code': code.code})])
162
163 _log.debug('Redirecting to {0}'.format(redirect_uri))
164
165 return redirect(request, location=redirect_uri)
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
172 form = AuthorizationForm(request.form)
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})
180
181
182 def access_token(request):
183 if request.GET.get('code'):
184 code = OAuthCode.query.filter(OAuthCode.code ==
185 request.GET.get('code')).first()
186
187 if code:
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
213 token = OAuthToken()
214 token.token = unicode(uuid4())
215 token.user = code.user
216 token.client = code.client
217 token.save()
218
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
224 access_token_data = {
225 'access_token': token.token,
226 'token_type': 'bearer',
227 'expires_in': exp_in}
228 return json_response(access_token_data, _disable_cors=True)
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'})