summaryrefslogtreecommitdiff
path: root/effireg
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2018-11-19 22:02:29 +0200
committerLars Wirzenius <liw@liw.fi>2018-11-19 22:02:29 +0200
commit64c3b3c54843b78f217aec79259b406637fc8ea5 (patch)
tree3e399c8f4832189cd5b2a611f4628da61d8d48ee /effireg
parenta580249c56e3dc79dea528156371e3ce01f2e857 (diff)
downloadeffi-reg-64c3b3c54843b78f217aec79259b406637fc8ea5.tar.gz
Add: login, logout
Diffstat (limited to 'effireg')
-rwxr-xr-xeffireg183
1 files changed, 179 insertions, 4 deletions
diff --git a/effireg b/effireg
index a81287a..ef12535 100755
--- a/effireg
+++ b/effireg
@@ -19,17 +19,35 @@ import json
import logging
import os
import sys
+import urllib
import uuid
import bottle
import requests
+COOKIE_NAME = 'effireg'
+
+
class EffiAPI:
- def __init__(self, app, apiurl):
+ _scopes = [
+ 'create',
+ 'update',
+ 'show',
+ 'delete',
+ 'super',
+ ]
+
+ def __init__(self, app, our_url, apiurl, client_id, client_secret):
self._add_routes(app)
+ self._our_url = our_url
self._apiurl = apiurl
+ self._auth_url = '{}/auth'.format(apiurl)
+ self._token_url = '{}/token'.format(apiurl)
+ self._client_id = client_id
+ self._client_secret = client_secret
+ self._sessions = Sessions()
def _add_routes(self, bottleapp):
routes = [
@@ -38,6 +56,21 @@ class EffiAPI:
'path': '/',
'callback': self._call(self._frontpage),
},
+ {
+ 'method': 'GET',
+ 'path': '/login',
+ 'callback': self._call(self._redirect_to_login),
+ },
+ {
+ 'method': 'GET',
+ 'path': '/logout',
+ 'callback': self._call(self._logout),
+ },
+ {
+ 'method': 'GET',
+ 'path': '/callback',
+ 'callback': self._call(self._auth_callback),
+ },
]
for route in routes:
@@ -51,11 +84,151 @@ class EffiAPI:
for h in r.headers:
logging.info('Request: headers: %s: %s', h, r.get_header(h))
logging.info('Request: body: %r', r.body.read())
- return callback()
+ r = callback()
+ logging.debug('Response: %r', r)
+ return r
return helper
def _frontpage(self):
- return "Effi REG!"
+ session = None
+ cookie = bottle.request.get_cookie(COOKIE_NAME)
+ if cookie:
+ session = self._sessions.find_by_cookie(cookie)
+
+ if session is None:
+ return self._not_logged_in_page()
+ return self._logged_in_page(cookie)
+
+ def _not_logged_in_page(self):
+ return '''
+ <p>You are NOT logged in.
+ <a href="/login">Log in</a></p>
+ '''
+ def _logged_in_page(self, cookie):
+ return '''
+ <p>You ARE logged in. Cookie is {}
+ <a href="/logout">Log out</a></p>
+ '''.format(cookie)
+
+ def _redirect_to_frontpage(self):
+ headers = {
+ 'Location': self._our_url,
+ }
+ logging.info('Redirecting with headers %s', headers)
+ return bottle.HTTPResponse(status=302 , headers=headers)
+
+ def _redirect_to_login(self):
+ session = self._sessions.new()
+ params = {
+ 'response_type': 'code',
+ 'scope': ' '.join(self._scopes),
+ 'client_id': self._client_id,
+ 'state': session.state,
+ 'redirect_uri': '{}/callback'.format(self._our_url),
+ }
+
+ auth_url = '{}?{}'.format(
+ self._auth_url, urllib.parse.urlencode(params))
+
+ headers = {
+ 'Location': auth_url,
+ }
+
+ logging.info('Redirecting with headers %s', headers)
+ return bottle.HTTPResponse(status=302 , headers=headers)
+
+ def _auth_callback(self):
+ code = bottle.request.query.get('code')
+ state = bottle.request.query.get('state')
+ if code is None or state is None:
+ return regesta_api.bad_request_response()
+
+ token = self._get_token_using_authz_code(code)
+
+ session = self._sessions.find_by_state(state)
+ if session is None:
+ return self._redirect_to_login()
+
+ session.token = token
+
+ logging.debug('Redirecting to / with cookie %s', session.cookie)
+ bottle.response.set_cookie(COOKIE_NAME, session.cookie, path='/')
+ bottle.redirect(self._our_url)
+ return None
+
+ headers = {
+ 'Location': self._our_url,
+ }
+ r = bottle.HTTPResponse(302, headers=headers)
+ r.set_cookie(COOKIE_NAME, session.cookie, path='/')
+ logging.debug('Redirecting to / with cookie %s', session.cookie)
+ return r
+
+ def _get_token_using_authz_code(self, code):
+ auth = (self._client_id, self._client_secret)
+ data = {
+ 'grant_type': 'authorization_code',
+ 'code': code,
+ }
+
+ url = self._token_url
+ logging.debug('Requesting token using authz code from %s', url)
+ r = requests.post(url, auth=auth, data=data)
+ if not r.ok:
+ logging.error(
+ 'Could not get token using authz code: %s %s',
+ r.status_code, r.text)
+ raise bottle.HTTPError(500)
+ obj = r.json()
+ return obj[u'access_token']
+
+ def _logout(self):
+ cookie = bottle.request.get_cookie(COOKIE_NAME)
+ if not cookie:
+ return self._redirect_to_frontpage()
+
+ session = self._sessions.find_by_cookie(cookie)
+ self._sessions.delete(session)
+
+ logging.debug('Redirecting to / without')
+ bottle.response.delete_cookie(COOKIE_NAME, path='/')
+ bottle.redirect(self._our_url)
+ return None
+
+
+class Session:
+
+ def __init__(self):
+ self.id = str(uuid.uuid4())
+ self.state = str(uuid.uuid4())
+ self.cookie = str(uuid.uuid4())
+ self.token = None
+
+
+class Sessions:
+
+ def __init__(self):
+ self._sessions = []
+
+ def new(self):
+ session = Session()
+ self._sessions.append(session)
+ return session
+
+ def delete(self, session):
+ self._sessions = [s for s in self._sessions if s.id != session.id]
+
+ def find_by_state(self, state):
+ return self._find(lambda s: s.state == state)
+
+ def find_by_cookie(self, cookie):
+ return self._find(lambda s: s.cookie == cookie)
+
+ def _find(self, is_this_it):
+ for session in self._sessions:
+ if is_this_it(session):
+ return session
+ return None
def main():
@@ -74,7 +247,9 @@ def main():
f.write(str(pid))
app = bottle.default_app()
- api = EffiAPI(app, config)
+ api = EffiAPI(
+ app, config['our-url'], config['api-url'],
+ config['client-id'], config['client-secret'])
app.run(host='127.0.0.1', port=8181)