From 64c3b3c54843b78f217aec79259b406637fc8ea5 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Mon, 19 Nov 2018 22:02:29 +0200 Subject: Add: login, logout --- effireg | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 179 insertions(+), 4 deletions(-) (limited to 'effireg') 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 ''' +

You are NOT logged in. + Log in

+ ''' + def _logged_in_page(self, cookie): + return ''' +

You ARE logged in. Cookie is {} + Log out

+ '''.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) -- cgit v1.2.1