Commit 8dfe8699 authored by Michael Völske's avatar Michael Völske

add openid login service

parent 2b5e6fda
#!/usr/bin/env python
# Webis OpenID Server
#
# This app messes with your pip. Run it in a container (tested with official
# python:3 image)
#
# Make sure these environment variables are defined:
ENV = ['OPENID_APP_ID',
'OPENID_APP_SECRET',
'OPENID_ISSUER',
'OPENID_CALLBACK_URL']
########################################################################
import os
import logging
import sys
import json
L = logging.getLogger('Webis OpenID')
L.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
L.addHandler(handler)
if not(all(x in os.environ for x in ENV)):
L.error("Make sure all evironment variables in %s are set", ENV)
sys.exit(1)
ENV = dict((k, os.environ[k]) for k in ENV)
CB_ROUTE = "/".join(ENV['OPENID_CALLBACK_URL'].split('//')[1].split(':')[-1].split('/')[1:])
L.info("Will send callbacks to /%s", CB_ROUTE)
L.info("Setting up...")
retval = os.system('pip install bottle==0.12.* oic==0.15.*')
if retval != 0:
L.error('Setup failed.')
sys.exit(retval)
L.info("Done setting up...")
########################################################################
from oic import rndstr
from oic.oic import Client
from oic.oic.message import RegistrationResponse, AuthorizationResponse
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
from oic.utils.http_util import Redirect
from bottle import run, route, request, response, redirect, abort
k_key = rndstr()
sessions = {}
def get_session(sid=None):
if sid is None:
sid = request.get_cookie('session', secret=k_key)
if sid is None:
sid = rndstr()
response.set_cookie('session', sid, secret=k_key)
if sid not in sessions:
sessions[sid] = {}
return sid, sessions[sid]
def setv(k, v, sid=None):
sid, s = get_session(sid)
s[k] = v
return sid
def getv(k, sid=None):
sid, s = get_session(sid)
return s.get(k, None)
def close_session(sid=None):
if sid is None:
sid = request.get_cookie('session', secret=k_key)
if sid is not None and sid in sessions:
del sessions[sid]
ALLOWED_OPS = ['k8s-login']
def get_client(session):
if 'client' in session:
L.info('Reusing client from session')
return session['client']
L.info('Building OIC client')
client = Client(client_authn_method=CLIENT_AUTHN_METHOD)
provider_info = client.provider_config(ENV['OPENID_ISSUER'])
info = {"client_id": ENV['OPENID_APP_ID'],
"client_secret": ENV['OPENID_APP_SECRET'],
"redirect_uris": [ENV['OPENID_CALLBACK_URL']]}
client_reg = RegistrationResponse(**info)
client.store_registration_info(client_reg)
session['client'] = client
return client
@route('/')
def index():
out = "<html><head><title>Choose Login op</title></head>" + \
"<body><p>Click below to continue:</p><ol>{ops}</ol></body></html>"
# maybe we'll have more of these someday.
out = out.format(
ops='\n'.join('<li><a href="{rt}">{name}</a></li>'.format(
rt=x, name=x
) for x in ALLOWED_OPS)
)
response.content_type = 'text/html; charset=utf8'
return out
@route('/k8s-login')
def k8s_login():
sid = setv('op', 'k8s-login')
setv('state', rndstr(), sid=sid)
setv('nonce', rndstr(), sid=sid)
sid, session = get_session(sid=sid)
client = get_client(session)
rq_args = {
"client_id": client.client_id,
"response_type": ['code'],
"scope": ['openid', 'offline_access', 'email', 'groups', 'profile'],
"nonce": session["nonce"],
"redirect_uri": client.registration_response["redirect_uris"][0],
"state": session["state"]
}
auth_req = client.construct_AuthorizationRequest(request_args=rq_args)
login_url = auth_req.request(client.authorization_endpoint)
redirect(login_url)
@route('/%s' % CB_ROUTE)
def callback():
op = getv('op')
#if op is None:
# abort(418, 'Nothing to see here')
#if op not in ALLOWED_OPS:
# abort(501, 'Operation %s is not supported' % op)
aresp = dict(request.query.decode())
sid, session = get_session()
if 'code' not in aresp or 'state' not in aresp or aresp['state'] != getv('state'):
abort(400, "Bad auth response. Got: %s." % aresp)
client = get_client(session)
aresp = client.parse_response(AuthorizationResponse, json.dumps(aresp))
L.info(aresp)
L.info(session)
L.info("Sending token request...")
resp = client.do_access_token_request(
state=aresp['state'], request_args={'code': aresp['code']},
auth_method='client_secret_basic')
itok = resp['id_token']
close_session()
response.content_type = 'text/plain; charset=utf8'
return CMDLINE_TEMPLATE.format(
u=itok['email'], url=itok['iss'], cid=ENV['OPENID_APP_ID'],
cs=ENV['OPENID_APP_SECRET'], rtok=resp['refresh_token'])
CMDLINE_TEMPLATE = """\
### All done. Paste the following into your terminal:
{{
kubectl config set-credentials "{u}" \\
--auth-provider=oidc \\
--auth-provider-arg=idp-issuer-url={url} \\
--auth-provider-arg=client-id={cid} \\
--auth-provider-arg=client-secret={cs} \\
--auth-provider-arg=refresh-token={rtok}
kubectl config set-cluster webis6 \\
--server=https://webis6.medien.uni-weimar.de:6443 \\
--insecure-skip-tls-verify=true
kubectl config set-context webis6 --cluster=webis6 --user="{u}"
kubectl config use-context webis6
}}
"""
run(host='0.0.0.0', port=42001, reloader=True)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment