diff options
| author | Simo Sorce <simo@redhat.com> | 2015-04-01 18:13:22 -0400 |
|---|---|---|
| committer | Simo Sorce <simo@redhat.com> | 2015-04-01 20:24:48 -0400 |
| commit | d2f4eb8ab433b45d503aa98e3028deab0c0b79c4 (patch) | |
| tree | 648ff843f70660702a9be74d6128c01fd368fe87 | |
| parent | 2e03a2254a27159dcbdb3618b642ba78f4432494 (diff) | |
| download | custodia-d2f4eb8ab433b45d503aa98e3028deab0c0b79c4.tar.gz custodia-d2f4eb8ab433b45d503aa98e3028deab0c0b79c4.tar.xz custodia-d2f4eb8ab433b45d503aa98e3028deab0c0b79c4.zip | |
Add simple secrets storage API to Root
This is a very simple implementation of a prototype API.
Anyone that has access to the server and causes an authentication
plugin to set the 'remote_user' value in the request, can retrieve
and store secrets,
Secrets are namespaced to the user requsteing them, so sharing secrets
between multiple users is not possible.
Secrets must to be of type "simple" and can only have one value.
The value can be anything that can be reprsented in json format.
It is recommended to pass a base64 encoded value.
| -rw-r--r-- | custodia.conf | 1 | ||||
| -rw-r--r-- | custodia/httpd/authenticators.py | 2 | ||||
| -rw-r--r-- | custodia/root.py | 77 |
3 files changed, 77 insertions, 3 deletions
diff --git a/custodia.conf b/custodia.conf index eb8d23e..e015fdd 100644 --- a/custodia.conf +++ b/custodia.conf @@ -9,7 +9,6 @@ gid = 48 [auth:header] handler = custodia.httpd.authenticators.SimpleHeaderAuth name = REMOTE_USER -value = simo [store:simple] handler = custodia.store.sqlite.SqliteStore diff --git a/custodia/httpd/authenticators.py b/custodia/httpd/authenticators.py index 8a1bff5..1b76287 100644 --- a/custodia/httpd/authenticators.py +++ b/custodia/httpd/authenticators.py @@ -59,7 +59,7 @@ class SimpleHeaderAuth(HTTPAuthenticator): return request['valid_auth'] = True - request['valid_header'] = value + request['remote_user'] = value class SimpleNULLAuth(HTTPAuthenticator): diff --git a/custodia/root.py b/custodia/root.py index d02557a..6a1a747 100644 --- a/custodia/root.py +++ b/custodia/root.py @@ -1,10 +1,85 @@ # Copyright (C) 2015 Custodia Project Contributors - see LICENSE file -import json from custodia.httpd.consumer import HTTPConsumer +from custodia.httpd.server import HTTPError +from custodia.store.interface import CSStoreError +import json +import os + + +class Secrets(HTTPConsumer): + + def _get_key(self, namespace, trail): + # pylint: disable=star-args + return os.path.join(namespace, 'keys', *trail) + + def _validate(self, value): + try: + msg = json.loads(value) + except Exception: + raise ValueError('Invalid JSON in payload') + if 'type' not in msg: + raise ValueError('Message type missing') + if msg['type'] != 'simple': + raise ValueError('Message type unknown') + if 'value' not in msg: + raise ValueError('Message value missing') + if len(msg.keys()) != 2: + raise ValueError('Unknown attributes in Message') + + def _namespace(self, request): + if 'remote_user' not in request: + raise HTTPError(403) + return request['remote_user'] + + def GET(self, request, response): + trail = request.get('trail', []) + ns = self._namespace(request) + if len(trail) == 0: + try: + self.root.store.list(request.get('query', + dict()).get('filter', '*')) + except CSStoreError: + raise HTTPError(500) + else: + key = self._get_key(ns, trail) + try: + response['output'] = self.root.store.get(key) + except CSStoreError: + raise HTTPError(500) + + def PUT(self, request, response): + trail = request.get('trail', []) + ns = self._namespace(request) + if len(trail) == 0: + raise HTTPError(405) + else: + content_type = request.get('headers', + dict()).get('Content-Type', '') + if content_type.split(';')[0].strip() != 'application/json': + raise HTTPError(400, 'Invalid Content-Type') + body = request.get('body') + if body is None: + raise HTTPError(400) + value = bytes(body).decode('utf-8') + try: + self._validate(value) + except ValueError as e: + raise HTTPError(400, str(e)) + + key = self._get_key(ns, trail) + try: + self.root.store.set(key, value) + except CSStoreError: + raise HTTPError(500) class Root(HTTPConsumer): + def __init__(self, *args, **kwargs): + super(Root, self).__init__(*args, **kwargs) + if self.store_name is not None: + self.add_sub('secrets', Secrets()) + def GET(self, request, response): return json.dumps({'message': "Quis custodiet ipsos custodes?"}) |
