From 478512d10ef861c05c7fb5e08b046d340636aef9 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Thu, 24 Jan 2019 10:10:11 +0200 Subject: Add: first step at creating client --- README | 11 +++++++++ effitool | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/README b/README index ccc3425..9320fb7 100644 --- a/README +++ b/README @@ -43,6 +43,17 @@ To check the status of the membership register: ./effitool status +To register a new API client: + + ./effitool register-client ID SECRET + +where `ID` is the client id and `SECRET` is the client secret. The new +client will automatically be allowed the scopes. Note that the +`credentials.conf` file is NOT updated: this command is for +registering API clients for other people, who do not have access to +the config file. + + Legalese ----------------------------------------------------------------------------- diff --git a/effitool b/effitool index 1032da5..74da7be 100755 --- a/effitool +++ b/effitool @@ -17,6 +17,7 @@ import argparse +import base64 import configparser import http import json @@ -27,6 +28,7 @@ import urllib.request CONFIG_FILENAME = os.path.expanduser('~/.config/effitool/credentials.conf') JSON = 'application/json' +URLENC = 'application/x-www-form-urlencoded' class Config: @@ -44,6 +46,12 @@ class Config: def servers(self): return [k for k in self._cp.keys() if k != 'DEFAULT'] + def get_default_server(self): + sections = self._cp.sections() + if len(sections) != 1: + raise Exception('Config must have exactly one server') + return sections[0] + def get(self, name): server = self._cp[name] return { @@ -76,6 +84,52 @@ class HTTPAPI: body = r.read() return json.loads(body) + def post_form(self, path, user, password, **kwargs): + url = self.url(path) + host, port, path = self.parse_url(url) + + data = urllib.parse.urlencode(kwargs).encode('UTF-8') + headers = { + 'Content-type': URLENC, + 'Authorization': self.get_authorization(user, password), + } + + req = urllib.request.Request( + url, data=data, headers=headers, method='POST') + r = urllib.request.urlopen(req) + + status = r.getcode() + if status != http.HTTPStatus.OK: + raise Exception('Got HTTP status {}'.format(status)) + + info = r.info() + ct = info.get_content_type() + if ct != JSON: + raise Exception('Response is not JSON: {}'.ct) + + body = r.read() + return json.loads(body) + + def parse_url(self, url): + parse = urllib.parse.urlparse(url) + if parse.scheme != 'https': + raise Exception( + 'URL scheme must be https, not {}'.format(parse.scheme)) + + if ':' in parse.netloc: + host, port = parse.netloc.split(':', 1) + port = int(port) + else: + host = parse.netloc + port = None + + return host, port, parse.path + + def get_authorization(self, user, password): + clear = '{}:{}'.format(user, password) + basic = base64.b64encode(clear.encode('UTF-8')) + return 'Basic {}'.format(basic.decode('UTF-8')) + def list_servers(args, config): for name in config.servers(): @@ -94,17 +148,41 @@ def status(args, config): print('server', name, obj['resources']) +def register_client(args, config): + new_client_id = args['client_id'] + new_client_secret = args['client_secret'] + name = config.get_default_server() + server = config.get(name) + url = server['url'] + client_id = server['client_id'] + client_secret = server['client_secret'] + api = HTTPAPI(url) + r = api.post_form( + '/token', client_id, client_secret, + grant_type='client_credentials', scope='xxx') + token = r['access_token'] + + print('NEED TO ACTUALLY CREATE CLIENT HERE') + assert 0 + + def process_args(config): subcommands = [ - ('list-servers', list_servers), - ('status', status), + ('list-servers', list_servers, []), + ('status', status, []), + ('register-client', register_client, [ + ('--client-id', {'required':True}), + ('--client-secret', {'required':True}), + ]), ] p = argparse.ArgumentParser() factory = p.add_subparsers() - for name, func in subcommands: + for name, func, args in subcommands: pp = factory.add_parser(name) + for name, kwargs in args: + pp.add_argument(name, **kwargs) pp.set_defaults(func=func) args = vars(p.parse_args()) -- cgit v1.2.1