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 webob
import exc
, Response
22 from urllib
import urlencode
23 from uuid
import uuid4
24 from datetime
import datetime
26 from mediagoblin
.tools
.response
import render_to_response
, redirect
27 from mediagoblin
.decorators
import require_active_login
28 from mediagoblin
.messages
import add_message
, SUCCESS
, ERROR
29 from mediagoblin
.tools
.translate
import pass_to_ugettext
as _
30 from mediagoblin
.plugins
.oauth
.models
import OAuthCode
, OAuthToken
, \
31 OAuthClient
, OAuthUserClient
32 from mediagoblin
.plugins
.oauth
.forms
import ClientRegistrationForm
, \
34 from mediagoblin
.plugins
.oauth
.tools
import require_client_auth
35 from mediagoblin
.plugins
.api
.tools
import json_response
37 _log
= logging
.getLogger(__name__
)
41 def register_client(request
):
43 Register an OAuth client
45 form
= ClientRegistrationForm(request
.form
)
47 if request
.method
== 'POST' and form
.validate():
48 client
= OAuthClient()
49 client
.name
= unicode(request
.form
['name'])
50 client
.description
= unicode(request
.form
['description'])
51 client
.type = unicode(request
.form
['type'])
52 client
.owner_id
= request
.user
.id
53 client
.redirect_uri
= unicode(request
.form
['redirect_uri'])
55 client
.generate_identifier()
56 client
.generate_secret()
60 add_message(request
, SUCCESS
, _('The client {0} has been registered!')\
64 return redirect(request
, 'mediagoblin.plugins.oauth.list_clients')
66 return render_to_response(
68 'oauth/client/register.html',
73 def list_clients(request
):
74 clients
= request
.db
.OAuthClient
.query
.filter(
75 OAuthClient
.owner_id
== request
.user
.id).all()
76 return render_to_response(request
, 'oauth/client/list.html',
81 def list_connections(request
):
82 connections
= OAuthUserClient
.query
.filter(
83 OAuthUserClient
.user
== request
.user
).all()
84 return render_to_response(request
, 'oauth/client/connections.html',
85 {'connections': connections
})
89 def authorize_client(request
):
90 form
= AuthorizationForm(request
.form
)
92 client
= OAuthClient
.query
.filter(OAuthClient
.id ==
93 form
.client_id
.data
).first()
96 _log
.error('''No such client id as received from client authorization
98 return exc
.HTTPBadRequest()
101 relation
= OAuthUserClient()
102 relation
.user_id
= request
.user
.id
103 relation
.client_id
= form
.client_id
.data
105 relation
.state
= u
'approved'
107 relation
.state
= u
'rejected'
109 return exc
.HTTPBadRequest
113 return exc
.HTTPFound(location
=form
.next
.data
)
115 return render_to_response(
117 'oauth/authorize.html',
123 @require_active_login
124 def authorize(request
, client
):
125 # TODO: Get rid of the JSON responses in this view, it's called by the
126 # user-agent, not the client.
127 user_client_relation
= OAuthUserClient
.query
.filter(
128 (OAuthUserClient
.user
== request
.user
)
129 & (OAuthUserClient
.client
== client
))
131 if user_client_relation
.filter(OAuthUserClient
.state
==
132 u
'approved').count():
135 if client
.type == u
'public':
136 if not client
.redirect_uri
:
137 return json_response({
140 [u
'Public clients MUST have a redirect_uri pre-set']},
143 redirect_uri
= client
.redirect_uri
145 if client
.type == u
'confidential':
146 redirect_uri
= request
.GET
.get('redirect_uri', client
.redirect_uri
)
148 return json_response({
150 'errors': [u
'Can not find a redirect_uri for client: {0}'\
151 .format(client
.name
)]}, _disable_cors
=True)
154 code
.code
= unicode(uuid4())
155 code
.user
= request
.user
159 redirect_uri
= ''.join([
162 urlencode({'code': code
.code
})])
164 _log
.debug('Redirecting to {0}'.format(redirect_uri
))
166 return exc
.HTTPFound(location
=redirect_uri
)
168 # Show prompt to allow client to access data
169 # - on accept: send the user agent back to the redirect_uri with the
171 # - on deny: send the user agent back to the redirect uri with error
173 form
= AuthorizationForm(request
.form
)
174 form
.client_id
.data
= client
.id
175 form
.next
.data
= request
.url
176 return render_to_response(
178 'oauth/authorize.html',
183 def access_token(request
):
184 if request
.GET
.get('code'):
185 code
= OAuthCode
.query
.filter(OAuthCode
.code
==
186 request
.GET
.get('code')).first()
189 if code
.client
.type == u
'confidential':
190 client_identifier
= request
.GET
.get('client_id')
192 if not client_identifier
:
193 return json_response({
194 'error': 'invalid_request',
196 'Missing client_id in request'})
198 client_secret
= request
.GET
.get('client_secret')
200 if not client_secret
:
201 return json_response({
202 'error': 'invalid_request',
204 'Missing client_secret in request'})
206 if not client_secret
== code
.client
.secret
or \
207 not client_identifier
== code
.client
.identifier
:
208 return json_response({
209 'error': 'invalid_client',
211 'The client_id or client_secret does not match the'
215 token
.token
= unicode(uuid4())
216 token
.user
= code
.user
217 token
.client
= code
.client
220 # expire time of token in full seconds
221 # timedelta.total_seconds is python >= 2.7 or we would use that
222 td
= token
.expires
- datetime
.now()
223 exp_in
= 86400*td
.days
+ td
.seconds
# just ignore µsec
225 access_token_data
= {
226 'access_token': token
.token
,
227 'token_type': 'bearer',
228 'expires_in': exp_in
}
229 return json_response(access_token_data
, _disable_cors
=True)
231 return json_response({
232 'error': 'invalid_request',
236 return json_response({
237 'error': 'invalid_request',
239 'Missing `code` parameter in request'})