import json import logging import logging.handlers import urllib import urllib.parse import bottle import requests import yaml client_id = 'ickweb' COOKIE = 'ickweb-session' handler = logging.handlers.SysLogHandler() logging.basicConfig(handlers=[handler]) def create_app(our_url, controller, client_secret): app = bottle.Bottle() api = API(controller) parts = urllib.parse.urlparse(our_url) cookie_path = parts.path @app.route('/favicon.ico') def favicon(): raise bottle.HTTPError(status=404) @app.route('/web') def root(): cookie = get_cookie() if cookie is None: return bottle.template('login') api.set_token(cookie) return front_page() def get_cookie(): return bottle.request.get_cookie(COOKIE) def front_page(): return bottle.template('index', token=api.get_token()) @app.route('/web/login') def login(): scopes = [ 'openid', 'uapi_version_get', 'uapi_status_get', 'uapi_projects_get', 'uapi_projects_post', 'uapi_projects_id_get', 'uapi_projects_id_put', 'uapi_projects_id_delete', 'uapi_pipelines_get', 'uapi_pipelines_id_get', 'uapi_pipelines_id_delete', 'uapi_projects_id_status_get', 'uapi_projects_id_status_put', 'uapi_pipelines_post', 'uapi_pipelines_id_put', 'uapi_builds_get', 'uapi_builds_id_get', 'uapi_builds_id_delete', 'uapi_logs_get', 'uapi_logs_id_get', 'uapi_blobs_id_get', 'uapi_workers_get', 'uapi_workers_id_get', 'uapi_notify_post', ] params = { 'response_type': 'code', 'scope': ' '.join(scopes), 'client_id': client_id, 'state': 'FIXME', 'redirect_uri': '{}/callback'.format(our_url), } url = '{}/auth?{}'.format(controller, urllib.parse.urlencode(params)) headers = { 'Location': url, } print('params:', params) return bottle.HTTPResponse(status=302, headers=headers) @app.route('/web/logout') def logout(): bottle.response.delete_cookie(COOKIE, path=cookie_path) bottle.redirect('/web') @app.route('/web/projects') def projects(): projects = api.get_projects() return bottle.template('projects', projects=projects) @app.route('/web/projects/') def show_project(name): project = api.get_project(name) params = { 'project': project, 'as_yaml': as_yaml(project), } return bottle.template('project', **params) @app.route('/web/builds') def builds(): builds = api.get_builds() return bottle.template('builds', builds=builds) @app.route('/web/logs/') def show_log(buildid): log = api.get_log(buildid) return bottle.template('log', buildid=buildid, log=log) @app.route('/web/callback') def callback(): logging.debug('/callback called') code = bottle.request.query['code'] print('code:', code) path = '/token' params = { 'grant_type': 'authorization_code', 'code': code, } auth = (client_id, client_secret) print('requesting token using code') r = api.POST(path, params, auth) obj = r.json() token = obj['access_token'] print('got access token:', token) bottle.response.set_cookie(COOKIE, token, path=cookie_path) bottle.redirect('/web') return app def as_yaml(obj): return yaml.safe_dump(obj, indent=4, default_flow_style=False) def string_representer(dumper, data): style = None if '\n' in data: style = '|' return dumper.represent_scalar('tag:yaml.org,2002:str', data, style=style) yaml.add_representer(str, string_representer, Dumper=yaml.SafeDumper) class API: def __init__(self, api_url): self._url = api_url self.set_token(None) def set_token(self, token): self._token = token def get_token(self): return self._token def POST(self, path, params, auth): url = self.url(path) print('POST: url:', url) headers = {} token = self.get_token() if token is not None: headers['Authorization'] = 'Bearer {}'.format(self.get_token()) r = requests.post(url, headers=headers, data=params, auth=auth) if not r.ok: raise bottle.HTTPError(status=r.status_code) return r def get_projects(self): url = self.url('/projects') headers = { 'Authorization': 'Bearer {}'.format(self.get_token()) } r = requests.get(url, headers=headers) if not r.ok: raise bottle.HTTPError(status=r.status_code) obj = r.json() return list(sorted(obj['projects'], key=lambda p: p['project'])) def get_project(self, name): url = self.url('/projects', name) headers = { 'Authorization': 'Bearer {}'.format(self.get_token()) } r = requests.get(url, headers=headers) if not r.ok: raise bottle.HTTPError(status=r.status_code) return r.json() def get_builds(self): url = self.url('/builds') headers = { 'Authorization': 'Bearer {}'.format(self.get_token()) } r = requests.get(url, headers=headers) if not r.ok: raise bottle.HTTPError(status=r.status_code) obj = r.json() return list(sorted(obj['builds'], key=lambda p: p['build_id'])) def get_log(self, buildid): url = self.url('/logs', buildid) headers = { 'Authorization': 'Bearer {}'.format(self.get_token()) } r = requests.get(url, headers=headers) if not r.ok: raise bottle.HTTPError(status=r.status_code) return r.text def url(self, *parts): return '{}{}'.format(self._url, '/'.join(parts))