summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2015-04-01 18:13:22 -0400
committerSimo Sorce <simo@redhat.com>2015-04-01 20:24:48 -0400
commitd2f4eb8ab433b45d503aa98e3028deab0c0b79c4 (patch)
tree648ff843f70660702a9be74d6128c01fd368fe87
parent2e03a2254a27159dcbdb3618b642ba78f4432494 (diff)
downloadcustodia-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.conf1
-rw-r--r--custodia/httpd/authenticators.py2
-rw-r--r--custodia/root.py77
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?"})