Debugged Castellan API with TatuKeyManager

This commit is contained in:
Pino de Candia 2017-12-08 00:15:18 +00:00
parent fa70477628
commit 0b1c82f016
9 changed files with 134 additions and 194 deletions

View File

@ -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

View File

@ -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())

View File

@ -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')

View File

@ -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

View File

@ -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)

109
tatu/castellano.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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 {