Fixed typos visisble, appplication
[mediagoblin.git] / mediagoblin / plugins / oauth / views.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import logging
18 import json
19
20 from webob import exc, Response
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.POST)
45
46 if request.method == 'POST' and form.validate():
47 client = OAuthClient()
48 client.name = unicode(request.POST['name'])
49 client.description = unicode(request.POST['description'])
50 client.type = unicode(request.POST['type'])
51 client.owner_id = request.user.id
52 client.redirect_uri = unicode(request.POST['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.POST)
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 exc.HTTPBadRequest()
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 exc.HTTPBadRequest
109
110 relation.save()
111
112 return exc.HTTPFound(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 exc.HTTPFound(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.POST)
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 token = OAuthToken()
189 token.token = unicode(uuid4())
190 token.user = code.user
191 token.client = code.client
192 token.save()
193
194 access_token_data = {
195 'access_token': token.token,
196 'token_type': 'bearer',
197 'expires_in':
198 (token.expires - datetime.now()).total_seconds()}
199 return json_response(access_token_data, _disable_cors=True)
200
201 error_data = {
202 'error': 'Incorrect code'}
203 return Response(json.dumps(error_data))