1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__
import absolute_import
, unicode_literals
22 from urllib
import urlencode
24 from tornado
.auth
import OAuth2Mixin
, AuthError
, _auth_return_future
25 from tornado
.httpclient
import AsyncHTTPClient
, HTTPClient
, HTTPRequest
26 from tornado
.options
import options
27 from tornado
.web
import asynchronous
, HTTPError
29 from flower
.views
import BaseHandler
30 from flower
.urls
import settings
33 class FlowerAuthHandler(BaseHandler
, OAuth2Mixin
):
34 _OAUTH_NO_CALLBACKS
= False
37 def _OAUTH_AUTHORIZE_URL(self
):
38 return os
.environ
['INDICO_FLOWER_AUTHORIZE_URL']
41 def _OAUTH_ACCESS_TOKEN_URL(self
):
42 return os
.environ
['INDICO_FLOWER_TOKEN_URL']
45 def get_authenticated_user(self
, redirect_uri
, code
, callback
):
46 http
= self
.get_auth_http_client()
48 'redirect_uri': redirect_uri
,
50 'client_id': os
.environ
['INDICO_FLOWER_CLIENT_ID'],
51 'client_secret': os
.environ
['INDICO_FLOWER_CLIENT_SECRET'],
52 'grant_type': 'authorization_code',
54 http
.fetch(self
._OAUTH
_ACCESS
_TOKEN
_URL
,
55 functools
.partial(self
._on
_access
_token
, callback
),
57 headers
={'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
62 def _on_access_token(self
, future
, response
):
64 future
.set_exception(AuthError('OAuth authentication error: {}'.format(response
)))
66 future
.set_result(json
.loads(response
.body
))
68 def get_auth_http_client(self
):
69 return AsyncHTTPClient()
73 redirect_uri
= 'http{}://{}:{}/login'.format('s' if 'ssl_options' in settings
else '',
74 options
.address
or 'localhost',
76 if self
.get_argument('code', False):
77 self
.get_authenticated_user(
78 redirect_uri
=redirect_uri
,
79 code
=self
.get_argument('code'),
80 callback
=self
._on
_auth
,
83 self
.authorize_redirect(
84 redirect_uri
=redirect_uri
,
85 client_id
=os
.environ
['INDICO_FLOWER_CLIENT_ID'],
88 extra_params
={'approval_prompt': 'auto'}
92 def _on_auth(self
, user
):
94 raise HTTPError(500, 'OAuth authentication failed')
95 access_token
= user
['access_token']
96 req
= HTTPRequest(os
.environ
['INDICO_FLOWER_USER_URL'],
97 headers
={'Authorization': 'Bearer ' + access_token
, 'User-agent': 'Tornado auth'},
99 response
= HTTPClient().fetch(req
)
100 payload
= json
.loads(response
.body
.decode('utf-8'))
101 if not payload
or not payload
['admin']:
102 raise HTTPError(403, 'Access denied')
103 self
.set_secure_cookie('user', 'Indico Admin')
104 self
.redirect(self
.get_argument('next', '/'))