1 # -*- coding: utf-8 -*-
2 # GNU MediaGoblin -- federated, autonomous media hosting
3 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
21 from urllib
import urlencode
22 from uuid
import uuid4
23 from datetime
import datetime
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
, \
33 from mediagoblin
.plugins
.oauth
.tools
import require_client_auth
34 from mediagoblin
.plugins
.api
.tools
import json_response
36 _log
= logging
.getLogger(__name__
)
40 def register_client(request
):
42 Register an OAuth client
44 form
= ClientRegistrationForm(request
.form
)
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'])
54 client
.generate_identifier()
55 client
.generate_secret()
59 add_message(request
, SUCCESS
, _('The client {0} has been registered!')\
63 return redirect(request
, 'mediagoblin.plugins.oauth.list_clients')
65 return render_to_response(
67 'oauth/client/register.html',
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',
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
})
88 def authorize_client(request
):
89 form
= AuthorizationForm(request
.form
)
91 client
= OAuthClient
.query
.filter(OAuthClient
.id ==
92 form
.client_id
.data
).first()
95 _log
.error('''No such client id as received from client authorization
100 relation
= OAuthUserClient()
101 relation
.user_id
= request
.user
.id
102 relation
.client_id
= form
.client_id
.data
104 relation
.state
= u
'approved'
106 relation
.state
= u
'rejected'
112 return redirect(request
, location
=form
.next
.data
)
114 return render_to_response(
116 'oauth/authorize.html',
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
))
130 if user_client_relation
.filter(OAuthUserClient
.state
==
131 u
'approved').count():
134 if client
.type == u
'public':
135 if not client
.redirect_uri
:
136 return json_response({
139 [u
'Public clients MUST have a redirect_uri pre-set']},
142 redirect_uri
= client
.redirect_uri
144 if client
.type == u
'confidential':
145 redirect_uri
= request
.GET
.get('redirect_uri', client
.redirect_uri
)
147 return json_response({
149 'errors': [u
'Can not find a redirect_uri for client: {0}'\
150 .format(client
.name
)]}, _disable_cors
=True)
153 code
.code
= unicode(uuid4())
154 code
.user
= request
.user
158 redirect_uri
= ''.join([
161 urlencode({'code': code
.code
})])
163 _log
.debug('Redirecting to {0}'.format(redirect_uri
))
165 return redirect(request
, location
=redirect_uri
)
167 # Show prompt to allow client to access data
168 # - on accept: send the user agent back to the redirect_uri with the
170 # - on deny: send the user agent back to the redirect uri with error
172 form
= AuthorizationForm(request
.form
)
173 form
.client_id
.data
= client
.id
174 form
.next
.data
= request
.url
175 return render_to_response(
177 'oauth/authorize.html',
182 def access_token(request
):
183 if request
.GET
.get('code'):
184 code
= OAuthCode
.query
.filter(OAuthCode
.code
==
185 request
.GET
.get('code')).first()
188 if code
.client
.type == u
'confidential':
189 client_identifier
= request
.GET
.get('client_id')
191 if not client_identifier
:
192 return json_response({
193 'error': 'invalid_request',
195 'Missing client_id in request'})
197 client_secret
= request
.GET
.get('client_secret')
199 if not client_secret
:
200 return json_response({
201 'error': 'invalid_request',
203 'Missing client_secret in request'})
205 if not client_secret
== code
.client
.secret
or \
206 not client_identifier
== code
.client
.identifier
:
207 return json_response({
208 'error': 'invalid_client',
210 'The client_id or client_secret does not match the'
214 token
.token
= unicode(uuid4())
215 token
.user
= code
.user
216 token
.client
= code
.client
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
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)
230 return json_response({
231 'error': 'invalid_request',
235 return json_response({
236 'error': 'invalid_request',
238 'Missing `code` parameter in request'})