From 0b1c82f01613ada626cc9d3989903c61e11945a3 Mon Sep 17 00:00:00 2001 From: Pino de Candia <giuseppe.decandia@gmail.com> Date: Fri, 8 Dec 2017 00:15:18 +0000 Subject: [PATCH] Debugged Castellan API with TatuKeyManager --- requirements.txt | 1 + tatu/api/app.py | 34 +++++---- tatu/castellan/__init__.py | 0 tatu/castellan/config.py | 52 -------------- tatu/castellan/tatu_key_manager.py | 68 ------------------ tatu/castellan/utils.py | 52 -------------- tatu/castellano.py | 109 +++++++++++++++++++++++++++++ tatu/db/models.py | 2 +- tatu/tests/test_app.py | 10 +-- 9 files changed, 134 insertions(+), 194 deletions(-) delete mode 100644 tatu/castellan/__init__.py delete mode 100644 tatu/castellan/config.py delete mode 100644 tatu/castellan/tatu_key_manager.py delete mode 100644 tatu/castellan/utils.py create mode 100644 tatu/castellano.py diff --git a/requirements.txt b/requirements.txt index 41bebec..bc03c0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. +castellan>=0.16.0 requests>=2.18.2 # Apache-2.0 python-keystoneclient!=1.8.0,!=2.1.0,>=1.7.0 # Apache-2.0 keystoneauth1>=2.7.0 # Apache-2.0 diff --git a/tatu/api/app.py b/tatu/api/app.py index 7c26ddd..7be99cc 100644 --- a/tatu/api/app.py +++ b/tatu/api/app.py @@ -1,22 +1,30 @@ import falcon import models +import os.path +from oslo_config import cfg +from tatu.castellano import validate_config as validate_castellan_config from tatu.db.persistence import SQLAlchemySessionManager -def create_app(sa): - api = falcon.API(middleware=[models.Logger(), sa]) - api.add_route('/authorities', models.Authorities()) - api.add_route('/authorities/{auth_id}', models.Authority()) - api.add_route('/usercerts', models.UserCerts()) - api.add_route('/usercerts/{user_id}/{fingerprint}', models.UserCert()) - api.add_route('/hostcerts', models.HostCerts()) - api.add_route('/hostcerts/{host_id}/{fingerprint}', models.HostCert()) - api.add_route('/hosttokens', models.Tokens()) - api.add_route('/novavendordata', models.NovaVendorData()) - return api +validate_castellan_config() +fname = 'tatu.conf' +CONF = cfg.CONF +if os.path.isfile(fname): + CONF(default_config_files=[fname]) +def create_app(sa): + api = falcon.API(middleware=[models.Logger(), sa]) + api.add_route('/authorities', models.Authorities()) + api.add_route('/authorities/{auth_id}', models.Authority()) + api.add_route('/usercerts', models.UserCerts()) + api.add_route('/usercerts/{user_id}/{fingerprint}', models.UserCert()) + api.add_route('/hostcerts', models.HostCerts()) + api.add_route('/hostcerts/{host_id}/{fingerprint}', models.HostCert()) + api.add_route('/hosttokens', models.Tokens()) + api.add_route('/novavendordata', models.NovaVendorData()) + return api def get_app(): - return create_app(SQLAlchemySessionManager()) + return create_app(SQLAlchemySessionManager()) def main(global_config, **settings): - return create_app(SQLAlchemySessionManager()) + return create_app(SQLAlchemySessionManager()) diff --git a/tatu/castellan/__init__.py b/tatu/castellan/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tatu/castellan/config.py b/tatu/castellan/config.py deleted file mode 100644 index 1576b57..0000000 --- a/tatu/castellan/config.py +++ /dev/null @@ -1,52 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from tatu.castellan import options as castellan -from oslo_config import cfg - -#from sahara.utils.openstack import base as utils - - -opts = [ - cfg.BoolOpt('use_barbican_key_manager', default=False, - help='Enable the usage of the OpenStack Key Management ' - 'service provided by barbican.'), -] - -castellan_opts = [ - cfg.StrOpt('barbican_api_endpoint', - help='The endpoint to use for connecting to the barbican ' - 'api controller. By default, castellan will use the ' - 'URL from the service catalog.'), - cfg.StrOpt('barbican_api_version', default='v1', - help='Version of the barbican API, for example: "v1"'), -] - -castellan_group = cfg.OptGroup(name='castellan', - title='castellan key manager options') - -CONF = cfg.CONF -CONF.register_group(castellan_group) -CONF.register_opts(opts) -CONF.register_opts(castellan_opts, group=castellan_group) - - -def validate_config(): - if CONF.use_barbican_key_manager: - # NOTE (elmiko) there is no need to set the api_class as castellan - # uses barbican by default. - #castellan.set_defaults(CONF, auth_endpoint=utils.retrieve_auth_url()) - castellan.set_defaults(CONF) - else: - castellan.set_defaults(CONF, api_class='tatu.castellan.' - 'tatu_key_manager.TatuKeyManager') \ No newline at end of file diff --git a/tatu/castellan/tatu_key_manager.py b/tatu/castellan/tatu_key_manager.py deleted file mode 100644 index 488c366..0000000 --- a/tatu/castellan/tatu_key_manager.py +++ /dev/null @@ -1,68 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from castellan.common.objects import passphrase as key -from castellan.key_manager import key_manager as km - - -"""tatu.castellan.tatu_key_manager -This module contains the KeyManager class that will be used by the -castellan library, it is not meant for direct usage within tatu. -""" - - -class TatuKeyManager(km.KeyManager): - """Tatu specific key manager - This manager is a thin wrapper around the secret being stored. It is - intended for backward compatible use only. It will not store keys - or generate UUIDs but instead return the secret that is being stored. - This behavior allows Tatu to continue storing secrets in its database - while using the Castellan key manager abstraction. - """ - def __init__(self, configuration=None): - pass - - def create_key(self, context, algorithm=None, length=0, - expiration=None, **kwargs): - """creates a key - algorithm, length, and expiration are unused by tatu keys. - """ - return key.Passphrase(passphrase=kwargs.get('passphrase', '')) - - def create_key_pair(self, *args, **kwargs): - pass - - def store(self, context, key, expiration=None, **kwargs): - """store a key - in normal usage a store_key will return the UUID of the key as - dictated by the key manager. Tatu would then store this UUID in - its database to use for retrieval. As tatu is not actually using - a key manager in this context it will return the key's payload for - storage. - """ - return key.get_encoded() - - def get(self, context, key_id, **kwargs): - """get a key - since tatu is not actually storing key UUIDs the key_id to this - function should actually be the key payload. this function will - simply return a new TatuKey based on that value. - """ - return key.Passphrase(passphrase=key_id) - - def delete(self, context, key_id, **kwargs): - """delete a key - as there is no external key manager, this function will not - perform any external actions. therefore, it won't change anything. - """ - pass \ No newline at end of file diff --git a/tatu/castellan/utils.py b/tatu/castellan/utils.py deleted file mode 100644 index 9799ff8..0000000 --- a/tatu/castellan/utils.py +++ /dev/null @@ -1,52 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from castellan.common.objects import passphrase -from castellan import key_manager - -from oslo_context import context - - -def delete_secret(id, ctx=None): - """delete a secret from the external key manager - :param id: The identifier of the secret to delete - :param ctx: The context, and associated authentication, to use with - this operation (defaults to the current context) - """ - if ctx is None: - ctx = context.current() - key_manager.API().delete(ctx, id) - - -def get_secret(id, ctx=None): - """get a secret associated with an id - :param id: The identifier of the secret to retrieve - :param ctx: The context, and associated authentication, to use with - this operation (defaults to the current context) - """ - if ctx is None: - ctx = context.current() - key = key_manager.API().get(ctx, id) - return key.get_encoded() - - -def store_secret(secret, ctx=None): - """store a secret and return its identifier - :param secret: The secret to store, this should be a string - :param ctx: The context, and associated authentication, to use with - this operation (defaults to the current context) - """ - if ctx is None: - ctx = context.current() - key = passphrase.Passphrase(secret) - return key_manager.API().store(ctx, key) \ No newline at end of file diff --git a/tatu/castellano.py b/tatu/castellano.py new file mode 100644 index 0000000..4e16c2b --- /dev/null +++ b/tatu/castellano.py @@ -0,0 +1,109 @@ +from castellan.common.objects.passphrase import Passphrase +from castellan.common.utils import credential_factory +from castellan.key_manager import API +from castellan.key_manager.key_manager import KeyManager +from castellan.options import set_defaults as set_castellan_defaults +from oslo_config import cfg + +opts = [ + cfg.BoolOpt('use_barbican_key_manager', default=False, + help='Enable the usage of the OpenStack Key Management ' + 'service provided by barbican.'), +] + +CONF = cfg.CONF +CONF.register_opts(opts, group='tatu') +_context = None +_api = None + +def validate_config(): + if CONF.tatu.use_barbican_key_manager: + set_castellan_defaults(CONF) + else: + set_castellan_defaults(CONF, + api_class='tatu.castellano.TatuKeyManager') + +def context(): + global _context + if _context is None and CONF.tatu.use_barbican_key_manager: + _context = credential_factory(conf=CONF) + return _context + +def api(): + global _api + if _api is None: + _api = API() + return _api + +def delete_secret(id, ctx=None): + """delete a secret from the external key manager + :param id: The identifier of the secret to delete + :param ctx: The context, and associated authentication, to use with + this operation (defaults to the current context) + """ + api().delete(ctx or context(), id) + +def get_secret(id, ctx=None): + """get a secret associated with an id + :param id: The identifier of the secret to retrieve + :param ctx: The context, and associated authentication, to use with + this operation (defaults to the current context) + """ + key = api().get(ctx or context(), id) + return key.get_encoded() + +def store_secret(secret, ctx=None): + """store a secret and return its identifier + :param secret: The secret to store, this should be a string + :param ctx: The context, and associated authentication, to use with + this operation (defaults to the current context) + """ + key = Passphrase(secret) + return api().store(ctx or context(), key) + +""" +This module contains the KeyManager class that will be used by the +castellan library, it is not meant for direct usage within tatu. +""" +class TatuKeyManager(KeyManager): + """Tatu specific key manager + This manager is a thin wrapper around the secret being stored. It is + intended for backward compatible use only. It will not store keys + or generate UUIDs but instead return the secret that is being stored. + This behavior allows Tatu to continue storing secrets in its database + while using the Castellan key manager abstraction. + """ + def __init__(self, configuration=None): + pass + + def create_key(self, context, algorithm=None, length=0, + expiration=None, **kwargs): + pass + + def create_key_pair(self, *args, **kwargs): + pass + + def store(self, context, key, expiration=None, **kwargs): + """store a key + in normal usage a store_key will return the UUID of the key as + dictated by the key manager. Tatu would then store this UUID in + its database to use for retrieval. As tatu is not actually using + a key manager in this context it will return the key's payload for + storage. + """ + return key.get_encoded() + + def get(self, context, key_id, **kwargs): + """get a key + since tatu is not actually storing key UUIDs the key_id to this + function should actually be the key payload. this function will + simply return a new TatuKey based on that value. + """ + return Passphrase(passphrase=key_id) + + def delete(self, context, key_id, **kwargs): + """delete a key + as there is no external key manager, this function will not + perform any external actions. therefore, it won't change anything. + """ + pass diff --git a/tatu/db/models.py b/tatu/db/models.py index b079412..16336fa 100644 --- a/tatu/db/models.py +++ b/tatu/db/models.py @@ -5,7 +5,7 @@ import sqlalchemy as sa from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.exc import IntegrityError import sshpubkeys -from tatu.castellan.utils import get_secret, store_secret +from tatu.castellano import get_secret, store_secret from tatu.utils import generateCert,random_uuid import uuid from Crypto.PublicKey import RSA diff --git a/tatu/tests/test_app.py b/tatu/tests/test_app.py index 2c6160b..8a022e8 100644 --- a/tatu/tests/test_app.py +++ b/tatu/tests/test_app.py @@ -178,14 +178,8 @@ def test_post_user_unknown_auth(client): assert response.status == falcon.HTTP_NOT_FOUND @pytest.mark.dependency(depends=['test_post_user']) -def test_post_same_user_same_key_fails(client): - # Show that using the same user ID and public key fails. - body = user_request() - response = client.simulate_post( - '/usercerts', - body=json.dumps(body) - ) - assert response.status == falcon.HTTP_CONFLICT +def test_post_same_user_and_key_returns_same_result(client): + test_post_user(client) def token_request(auth=auth_id, host=host_id): return {