# Copyright (C) 2015 Custodia Project Contributors - see LICENSE file from __future__ import absolute_import import logging import os import unittest from base64 import b64encode from custodia import log from custodia.compat import configparser from custodia.httpd.authorizers import UserNameSpace from custodia.plugin import HTTPError from custodia.secrets import Secrets from custodia.store.sqlite import SqliteStore CONFIG = u""" [store:sqlite] dburi = testdb.sqlite [authz:secrets] [authz:user] """ class SecretsTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.parser = configparser.ConfigParser( interpolation=configparser.ExtendedInterpolation() ) cls.parser.read_string(CONFIG) cls.log_handlers = log.auditlog.logger.handlers[:] log.auditlog.logger.handlers = [logging.NullHandler()] cls.secrets = Secrets(cls.parser, 'authz:secrets') cls.secrets.root.store = SqliteStore(cls.parser, 'store:sqlite') cls.authz = UserNameSpace(cls.parser, 'authz:user') @classmethod def tearDownClass(cls): log.auditlog.logger.handlers = cls.log_handlers try: os.unlink(cls.secrets.root.store.dburi) except OSError: pass def check_authz(self, req): req['client_id'] = 'test' req['path'] = '/'.join([''] + req.get('trail', [])) if self.authz.handle(req) is False: raise HTTPError(403) def DELETE(self, req, rep): self.check_authz(req) self.secrets.DELETE(req, rep) def GET(self, req, rep): self.check_authz(req) self.secrets.GET(req, rep) def POST(self, req, rep): self.check_authz(req) self.secrets.POST(req, rep) def PUT(self, req, rep): self.check_authz(req) self.secrets.PUT(req, rep) def test_0_LISTkey_404(self): req = {'remote_user': 'test', 'trail': ['test', '']} rep = {} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 404) def test_1_PUTKey(self): req = {'headers': {'Content-Type': 'application/json'}, 'remote_user': 'test', 'trail': ['test', 'key1'], 'body': '{"type":"simple","value":"1234"}'.encode('utf-8')} rep = {} self.PUT(req, rep) def test_2_GETKey(self): req = {'remote_user': 'test', 'trail': ['test', 'key1']} rep = {'headers': {}} self.GET(req, rep) self.assertEqual(rep['output'], {"type": "simple", "value": "1234"}) def test_3_LISTKeys(self): req = {'remote_user': 'test', 'trail': ['test', '']} rep = {'headers': {}} self.GET(req, rep) self.assertEqual(rep['output'], ["key1"]) def test_4_PUTKey_errors_400_1(self): req = {'headers': {'Content-Type': 'text/plain'}, 'remote_user': 'test', 'trail': ['test', 'key2'], 'body': '{"type":"simple","value":"2345"}'.encode('utf-8')} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 400) def test_4_PUTKey_errors_400_2(self): req = {'headers': {'Content-Type': 'text/plain'}, 'remote_user': 'test', 'trail': ['test', 'key2']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 400) def test_4_PUTKey_errors_400_3(self): req = {'headers': {'Content-Type': 'text/plain'}, 'remote_user': 'test', 'trail': ['test', 'key2'], 'body': '{"type":}"simple","value":"2345"}'.encode('utf-8')} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 400) def test_4_PUTKey_errors_403(self): req = {'headers': {'Content-Type': 'application/json; charset=utf-8'}, 'remote_user': 'test', 'trail': ['case', 'key2'], 'body': '{"type":"simple","value":"2345"}'.encode('utf-8')} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 403) def test_4_PUTKey_errors_404(self): req = {'headers': {'Content-Type': 'application/json; charset=utf-8'}, 'remote_user': 'test', 'trail': ['test', 'more', 'key1'], 'body': '{"type":"simple","value":"1234"}'.encode('utf-8')} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 404) def test_4_PUTKey_errors_405(self): req = {'headers': {'Content-Type': 'application/json; charset=utf-8'}, 'remote_user': 'test', 'trail': ['test', 'key2', ''], 'body': '{"type":"simple","value":"2345"}'.encode('utf-8')} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 405) def test_4_PUTKey_errors_409(self): req = {'headers': {'Content-Type': 'application/json; charset=utf-8'}, 'remote_user': 'test', 'trail': ['test', 'key3'], 'body': '{"type":"simple","value":"2345"}'.encode('utf-8')} rep = {'headers': {}} self.PUT(req, rep) with self.assertRaises(HTTPError) as err: self.PUT(req, rep) self.assertEqual(err.exception.code, 409) def test_5_GETKey_errors_403(self): req = {'remote_user': 'case', 'trail': ['test', 'key1']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 403) def test_5_GETkey_errors_404(self): req = {'remote_user': 'test', 'trail': ['test', 'key0']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 404) def test_5_GETkey_errors_406(self): req = {'remote_user': 'test', 'query': {'type': 'complex'}, 'trail': ['test', 'key1']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 406) def test_6_LISTkeys_errors_404_1(self): req = {'remote_user': 'test', 'trail': ['test', 'case', '']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 404) def test_6_LISTkeys_errors_406_1(self): req = {'remote_user': 'test', 'query': {'type': 'invalid'}, 'trail': ['test', '']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 406) def test_7_DELETEKey(self): req = {'remote_user': 'test', 'trail': ['test', 'key1']} rep = {'headers': {}} self.DELETE(req, rep) def test_7_DELETEKey_errors_403(self): req = {'remote_user': 'case', 'trail': ['test', 'key1']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.DELETE(req, rep) self.assertEqual(err.exception.code, 403) def test_7_DELETEKey_errors_404(self): req = {'remote_user': 'test', 'trail': ['test', 'nokey']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.DELETE(req, rep) self.assertEqual(err.exception.code, 404) def test_7_DELETEKey_errors_405(self): req = {'remote_user': 'test'} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.DELETE(req, rep) self.assertEqual(err.exception.code, 405) def test_8_CREATEcont(self): req = {'remote_user': 'test', 'trail': ['test', 'container', '']} rep = {'headers': {}} self.POST(req, rep) self.assertEqual(rep['code'], 201) def test_8_CREATEcont_erros_403(self): req = {'remote_user': 'case', 'trail': ['test', 'container', '']} rep = {} with self.assertRaises(HTTPError) as err: self.POST(req, rep) self.assertEqual(err.exception.code, 403) def test_8_CREATEcont_erros_404(self): req = {'remote_user': 'test', 'trail': ['test', 'mid', 'container', '']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.POST(req, rep) self.assertEqual(err.exception.code, 404) def test_8_CREATEcont_erros_405(self): req = {'remote_user': 'test', 'trail': ['test', 'container']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.POST(req, rep) self.assertEqual(err.exception.code, 405) def test_8_CREATEcont_already_exists(self): req = {'remote_user': 'test', 'trail': ['test', 'exists', '']} rep = {} self.POST(req, rep) self.assertEqual(rep['code'], 201) # Try to create the container again self.POST(req, rep) self.assertEqual(rep['code'], 200) def test_8_DESTROYcont(self): req = {'remote_user': 'test', 'trail': ['test', 'container', '']} rep = {'headers': {}} self.DELETE(req, rep) self.assertEqual(rep['code'], 204) def test_8_DESTROYcont_erros_403(self): req = {'remote_user': 'case', 'trail': ['test', 'container', '']} rep = {} with self.assertRaises(HTTPError) as err: self.DELETE(req, rep) self.assertEqual(err.exception.code, 403) def test_8_DESTROYcont_erros_404(self): req = {'remote_user': 'test', 'trail': ['test', 'mid', 'container', '']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.DELETE(req, rep) self.assertEqual(err.exception.code, 404) def test_8_DESTROYcont_erros_409(self): self.test_1_PUTKey() req = {'remote_user': 'test', 'trail': ['test', '']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.DELETE(req, rep) self.assertEqual(err.exception.code, 409) def test_9_0_PUTRawKey(self): req = {'headers': {'Content-Type': 'application/octet-stream'}, 'remote_user': 'test', 'trail': ['test', 'rawkey'], 'body': '1234'.encode('utf-8')} rep = {'headers': {}} self.PUT(req, rep) def test_9_1_GETRawKey(self): req = {'headers': {'Accept': 'application/octet-stream'}, 'remote_user': 'test', 'trail': ['test', 'rawkey']} rep = {'headers': {}} self.GET(req, rep) self.assertEqual(rep['output'], '1234'.encode('utf-8')) def test_9_2_GETKey(self): req = {'remote_user': 'test', 'trail': ['test', 'rawkey']} rep = {'headers': {}} self.GET(req, rep) self.assertEqual(rep['output'], {"type": "simple", "value": b64encode(b'1234').decode('utf-8')}) def test_10_LIST_subcontainer_and_keys(self): # Create a container req = {'remote_user': 'test', 'trail': ['test', 'container_a', '']} rep = {'headers': {}} self.POST(req, rep) self.assertEqual(rep['code'], 201) # Create a subcontainer over the parent container req = {'remote_user': 'test', 'trail': ['test', 'container_a', 'subcontainer', '']} rep = {'headers': {}} self.POST(req, rep) self.assertEqual(rep['code'], 201) # Create a secret over the parent container req = {'headers': {'Content-Type': 'application/json'}, 'remote_user': 'test', 'trail': ['test', 'container_a', 'key2'], 'body': '{"type":"simple","value":"1234"}'.encode('utf-8')} rep = {} self.PUT(req, rep) # Retrieve the container_a data req = {'remote_user': 'test', 'trail': ['test', 'container_a', '']} rep = {'headers': {}} self.GET(req, rep) # Verify that we can now distinguish between a subcontainer # and a key self.assertEqual(rep['output'], ['key2', 'subcontainer/']) # Clean up req = {'remote_user': 'test', 'trail': ['test', 'container_a', 'key2']} self.DELETE(req, rep) self.assertEqual(rep['code'], 204) req = {'remote_user': 'test', 'trail': ['test', 'container_a', 'subcontainer', '']} self.DELETE(req, rep) self.assertEqual(rep['code'], 204) req = {'remote_user': 'test', 'trail': ['test', 'container_a', '']} self.DELETE(req, rep) self.assertEqual(rep['code'], 204) def test_11_GET_container(self): # Create a container req = {'remote_user': 'test', 'trail': ['test', 'container_a', '']} rep = {'headers': {}} self.POST(req, rep) self.assertEqual(rep['code'], 201) req = {'remote_user': 'test', 'trail': ['test', 'container_a', '']} rep = {'headers': {}} self.GET(req, rep) self.assertEqual(rep['output'], []) req = {'remote_user': 'test', 'trail': ['test', 'container_a']} rep = {'headers': {}} with self.assertRaises(HTTPError) as err: self.GET(req, rep) self.assertEqual(err.exception.code, 406) req = {'remote_user': 'test', 'trail': ['test', 'container_a', '']} self.DELETE(req, rep) self.assertEqual(rep['code'], 204)