4 space indentation

This commit is contained in:
Pino de Candia 2017-12-08 15:04:44 -06:00
parent 95c0f1011c
commit 7812e1e8b6
16 changed files with 941 additions and 676 deletions

View File

@ -1,4 +1,15 @@
#!/usr/bin/env python
# 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.
import json
import requests

View File

@ -1,4 +1,16 @@
#!/usr/bin/env python
# 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.
import sys
import json
import yaml

View File

@ -1,3 +1,15 @@
# 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.
import json
import requests
import os

View File

@ -1,4 +1,16 @@
#!/usr/bin/env python
# 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.
import argparse
import json
import requests

View File

@ -1,4 +1,16 @@
#!/usr/bin/env python
# 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.
import argparse
import json
import os

View File

@ -1,4 +1,16 @@
#!/usr/bin/env python
# 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.
import sys
import json
import yaml

View File

@ -1,3 +1,15 @@
# 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.
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break

View File

@ -1,3 +1,15 @@
# 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.
import falcon
import models
import os.path
@ -11,6 +23,7 @@ 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())
@ -23,8 +36,10 @@ def create_app(sa):
api.add_route('/novavendordata', models.NovaVendorData())
return api
def get_app():
return create_app(SQLAlchemySessionManager())
def main(global_config, **settings):
return create_app(SQLAlchemySessionManager())

View File

@ -1,3 +1,15 @@
# 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.
import falcon
import json
import logging
@ -5,6 +17,7 @@ import uuid
from tatu.db import models as db
from Crypto.PublicKey import RSA
def validate_uuid(map, key):
try:
# Verify it's a valid UUID, then convert to canonical string representation
@ -14,6 +27,7 @@ def validate_uuid(map, key):
msg = '{} is not a valid UUID'.format(map[key])
raise falcon.HTTPBadRequest('Bad request', msg)
def validate_uuids(req, params):
id_keys = ['token_id', 'auth_id', 'host_id', 'user_id', 'project-id', 'instance-id']
if req.method in ('POST', 'PUT'):
@ -24,6 +38,7 @@ def validate_uuids(req, params):
if key in params:
validate_uuid(params, key)
def validate(req, resp, resource, params):
if req.content_length:
# Store the body since we cannot read the stream again later
@ -32,6 +47,7 @@ def validate(req, resp, resource, params):
raise falcon.HTTPBadRequest('The POST/PUT request is missing a body.')
validate_uuids(req, params)
class Logger(object):
def __init__(self):
self.logger = logging.getLogger(__name__)
@ -47,8 +63,8 @@ class Logger(object):
req.body if hasattr(req, 'body') else 'None',
resp.status, resp.location, resp.body))
class Authorities(object):
class Authorities(object):
@falcon.before(validate)
def on_post(self, req, resp):
try:
@ -61,8 +77,8 @@ class Authorities(object):
resp.status = falcon.HTTP_201
resp.location = '/authorities/' + req.body['auth_id']
class Authority(object):
class Authority(object):
@falcon.before(validate)
def on_get(self, req, resp, auth_id):
auth = db.getAuthority(self.session, auth_id)
@ -81,8 +97,8 @@ class Authority(object):
resp.body = json.dumps(body)
resp.status = falcon.HTTP_OK
class UserCerts(object):
class UserCerts(object):
@falcon.before(validate)
def on_post(self, req, resp):
# TODO: validation
@ -98,8 +114,8 @@ class UserCerts(object):
resp.status = falcon.HTTP_201
resp.location = '/usercerts/' + user.user_id + '/' + user.fingerprint
class UserCert(object):
class UserCert(object):
@falcon.before(validate)
def on_get(self, req, resp, user_id, fingerprint):
user = db.getUserCert(self.session, user_id, fingerprint)
@ -115,6 +131,7 @@ class UserCert(object):
resp.body = json.dumps(body)
resp.status = falcon.HTTP_OK
def hostToJson(host):
return json.dumps({
'host_id': host.host_id,
@ -123,8 +140,8 @@ def hostToJson(host):
'key-cert.pub': host.cert,
})
class HostCerts(object):
class HostCerts(object):
@falcon.before(validate)
def on_post(self, req, resp):
# Note that we could have found the host_id using the token_id.
@ -142,8 +159,8 @@ class HostCerts(object):
resp.status = falcon.HTTP_201
resp.location = '/hostcerts/' + host.host_id + '/' + host.fingerprint
class HostCert(object):
class HostCert(object):
@falcon.before(validate)
def on_get(self, req, resp, host_id, fingerprint):
host = db.getHostCert(self.session, host_id, fingerprint)
@ -153,8 +170,8 @@ class HostCert(object):
resp.body = hostToJson(host)
resp.status = falcon.HTTP_OK
class Tokens(object):
class Tokens(object):
@falcon.before(validate)
def on_post(self, req, resp):
try:
@ -169,8 +186,8 @@ class Tokens(object):
resp.status = falcon.HTTP_201
resp.location = '/hosttokens/' + token.token_id
class NovaVendorData(object):
class NovaVendorData(object):
@falcon.before(validate)
def on_post(self, req, resp):
# An example of the data nova sends to vendordata services:

View File

@ -1,3 +1,15 @@
# 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.passphrase import Passphrase
from castellan.common.utils import credential_factory
from castellan.key_manager import API
@ -16,6 +28,7 @@ CONF.register_opts(opts, group='tatu')
_context = None
_api = None
def validate_config():
if CONF.tatu.use_barbican_key_manager:
set_castellan_defaults(CONF)
@ -23,18 +36,21 @@ def validate_config():
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
@ -43,6 +59,7 @@ def delete_secret(id, ctx=None):
"""
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
@ -52,6 +69,7 @@ def get_secret(id, ctx=None):
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
@ -61,10 +79,13 @@ def store_secret(secret, ctx=None):
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
@ -73,6 +94,7 @@ class TatuKeyManager(KeyManager):
This behavior allows Tatu to continue storing secrets in its database
while using the Castellan key manager abstraction.
"""
def __init__(self, configuration=None):
pass

View File

@ -1,3 +1,15 @@
# 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 datetime import datetime
import falcon
import os
@ -6,12 +18,13 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import IntegrityError
import sshpubkeys
from tatu.castellano import get_secret, store_secret
from tatu.utils import generateCert,random_uuid
from tatu.utils import generateCert, random_uuid
import uuid
from Crypto.PublicKey import RSA
Base = declarative_base()
class Authority(Base):
__tablename__ = 'authorities'
@ -19,15 +32,19 @@ class Authority(Base):
user_key = sa.Column(sa.Text)
host_key = sa.Column(sa.Text)
def getAuthority(session, auth_id):
return session.query(Authority).get(auth_id)
def getAuthUserKey(auth):
return get_secret(auth.user_key)
def getAuthHostKey(auth):
return get_secret(auth.host_key)
def createAuthority(session, auth_id):
user_key = RSA.generate(2048).exportKey('PEM')
user_secret_id = store_secret(user_key)
@ -43,6 +60,7 @@ def createAuthority(session, auth_id):
raise falcon.HTTPConflict("This certificate authority already exists.")
return auth
class UserCert(Base):
__tablename__ = 'user_certs'
@ -51,9 +69,11 @@ class UserCert(Base):
auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id'))
cert = sa.Column(sa.Text)
def getUserCert(session, user_id, fingerprint):
return session.query(UserCert).get([user_id, fingerprint])
def createUserCert(session, user_id, auth_id, pub):
# Retrieve the authority's private key and generate the certificate
auth = getAuthority(session, auth_id)
@ -76,6 +96,7 @@ def createUserCert(session, user_id, auth_id, pub):
session.commit()
return user
class Token(Base):
__tablename__ = 'tokens'
@ -88,12 +109,13 @@ class Token(Base):
date_used = sa.Column(sa.DateTime, default=datetime.min)
fingerprint_used = sa.Column(sa.String(36))
def createToken(session, host_id, auth_id, hostname):
# Validate the certificate authority
auth = getAuthority(session, auth_id)
if auth is None:
raise falcon.HTTPNotFound(description='No Authority found with that ID')
#Check whether a token was already created for this host_id
# Check whether a token was already created for this host_id
try:
token = session.query(Token).filter(Token.host_id == host_id).one()
if token is not None:
@ -108,6 +130,7 @@ def createToken(session, host_id, auth_id, hostname):
session.commit()
return token
class HostCert(Base):
__tablename__ = 'host_certs'
@ -119,9 +142,11 @@ class HostCert(Base):
cert = sa.Column(sa.Text)
hostname = sa.Column(sa.String(36))
def getHostCert(session, host_id, fingerprint):
return session.query(HostCert).get([host_id, fingerprint])
def createHostCert(session, token_id, host_id, pub):
token = session.query(Token).get(token_id)
if token is None:
@ -155,7 +180,7 @@ def createHostCert(session, token_id, host_id, pub):
fingerprint=fingerprint,
auth_id=token.auth_id,
token_id=token_id,
pubkey = pub,
pubkey=pub,
cert=cert,
hostname=token.hostname)
session.add(host)

View File

@ -1,3 +1,15 @@
# 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.
import os
from sqlalchemy import create_engine
@ -7,7 +19,8 @@ from tatu.db.models import Base
def get_url():
return os.getenv("DATABASE_URL", "sqlite:///development.db")
#return os.getenv("DATABASE_URL", "sqlite:///:memory:")
# return os.getenv("DATABASE_URL", "sqlite:///:memory:")
class SQLAlchemySessionManager:
"""

View File

@ -1,3 +1,15 @@
# 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.
import json
import requests
import os
@ -8,6 +20,7 @@ import uuid
server = 'http://172.24.4.1:18322'
def vendordata_request(instance_id, project_id, hostname):
return {
'instance-id': instance_id,
@ -15,6 +28,7 @@ def vendordata_request(instance_id, project_id, hostname):
'hostname': hostname
}
def host_request(token, host, pub_key):
return {
'token_id': token,
@ -22,6 +36,7 @@ def host_request(token, host, pub_key):
'key.pub': pub_key
}
def test_host_certificate_generation():
project_id = random_uuid()
response = requests.post(

View File

@ -1,3 +1,15 @@
# 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 oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
@ -14,15 +26,15 @@ LOG = logging.getLogger(__name__)
CONF = cfg.CONF
DOMAIN = 'tatu'
class NotificationEndpoint(object):
class NotificationEndpoint(object):
filter_rule = oslo_messaging.NotificationFilter(
publisher_id='^identity.*',
event_type='^identity.project.created')
def __init__(self):
self.engine = create_engine(get_url())
#Base.metadata.create_all(self.engine)
# Base.metadata.create_all(self.engine)
self.Session = scoped_session(sessionmaker(self.engine))
def info(self, ctxt, publisher_id, event_type, payload, metadata):
@ -46,6 +58,7 @@ class NotificationEndpoint(object):
else:
LOG.error("Status update or unknown")
def main():
logging.register_options(CONF)
extra_log_level_defaults = ['tatu=DEBUG', '__main__=DEBUG']
@ -74,5 +87,6 @@ def main():
server.stop()
server.wait()
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,4 +1,14 @@
# coding=utf-8
# 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.
import json
import falcon
from falcon import testing
@ -12,15 +22,18 @@ from Crypto.PublicKey import RSA
import sshpubkeys
import time
@pytest.fixture
def db():
return SQLAlchemySessionManager()
@pytest.fixture
def client(db):
api = create_app(db)
return testing.TestClient(api)
token_id = ''
host_id = random_uuid()
@ -36,6 +49,7 @@ user_fingerprint = sshpubkeys.SSHKey(user_pub_key).hash_md5()
auth_id = random_uuid()
auth_user_pub_key = None
@pytest.mark.dependency()
def test_post_authority(client, auth_id=auth_id):
body = {
@ -48,6 +62,7 @@ def test_post_authority(client, auth_id=auth_id):
assert response.status == falcon.HTTP_CREATED
assert response.headers['location'] == '/authorities/' + auth_id
@pytest.mark.dependency(depends=['test_post_authority'])
def test_post_authority_duplicate(client):
body = {
@ -59,12 +74,14 @@ def test_post_authority_duplicate(client):
)
assert response.status == falcon.HTTP_CONFLICT
def test_post_no_body(client):
for path in ['/authorities', '/usercerts', '/hosttokens',
'/hostcerts', '/novavendordata']:
response = client.simulate_post(path)
assert response.status == falcon.HTTP_BAD_REQUEST
def test_post_empty_body(client):
bodystr = json.dumps({})
for path in ['/authorities', '/usercerts', '/hosttokens',
@ -72,6 +89,7 @@ def test_post_empty_body(client):
response = client.simulate_post(path, body=bodystr)
assert response.status == falcon.HTTP_BAD_REQUEST
def test_post_authority_bad_uuid(client):
body = {
'auth_id': 'foobar',
@ -82,6 +100,7 @@ def test_post_authority_bad_uuid(client):
)
assert response.status == falcon.HTTP_BAD_REQUEST
@pytest.mark.dependency(depends=['test_post_authority'])
def test_get_authority(client):
response = client.simulate_get('/authorities/' + auth_id)
@ -95,14 +114,17 @@ def test_get_authority(client):
assert 'user_key' not in body
assert 'host_key' not in body
def test_get_authority_doesnt_exist(client):
response = client.simulate_get('/authorities/' + random_uuid())
assert response.status == falcon.HTTP_NOT_FOUND
def test_get_authority_with_bad_uuid(client):
response = client.simulate_get('/authorities/foobar')
assert response.status == falcon.HTTP_BAD_REQUEST
def user_request(auth=auth_id, user_id=user_id, pub_key=user_pub_key):
return {
'user_id': user_id,
@ -110,6 +132,7 @@ def user_request(auth=auth_id, user_id=user_id, pub_key=user_pub_key):
'key.pub': pub_key
}
def test_post_user_bad_uuid(client):
for key in ['user_id', 'auth_id']:
body = user_request()
@ -120,6 +143,7 @@ def test_post_user_bad_uuid(client):
)
assert response.status == falcon.HTTP_BAD_REQUEST
@pytest.mark.dependency(depends=['test_post_authority'])
def test_post_user(client):
body = user_request()
@ -134,6 +158,7 @@ def test_post_user(client):
assert location[2] == body['user_id']
assert location[3] == sshpubkeys.SSHKey(body['key.pub']).hash_md5()
@pytest.mark.dependency(depends=['test_post_user'])
def test_get_user(client):
response = client.simulate_get('/usercerts/' + user_id + '/' + user_fingerprint)
@ -145,14 +170,17 @@ def test_get_user(client):
assert 'key-cert.pub' in body
assert body['auth_id'] == auth_id
def test_get_user_doesnt_exist(client):
response = client.simulate_get('/usercerts/' + random_uuid() + '/' + user_fingerprint)
assert response.status == falcon.HTTP_NOT_FOUND
def test_get_user_with_bad_uuid(client):
response = client.simulate_get('/usercerts/foobar/' + user_fingerprint)
assert response.status == falcon.HTTP_BAD_REQUEST
@pytest.mark.dependency(depends=['test_post_user'])
def test_post_second_cert_same_user(client):
key = RSA.generate(2048)
@ -169,6 +197,7 @@ def test_post_second_cert_same_user(client):
assert location[2] == user_id
assert location[3] == sshpubkeys.SSHKey(pub_key).hash_md5()
def test_post_user_unknown_auth(client):
body = user_request(auth=random_uuid())
response = client.simulate_post(
@ -177,10 +206,12 @@ 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_and_key_returns_same_result(client):
test_post_user(client)
def token_request(auth=auth_id, host=host_id):
return {
'host_id': host,
@ -188,6 +219,7 @@ def token_request(auth=auth_id, host=host_id):
'hostname': 'testname.local'
}
def host_request(token, host=host_id, pub_key=host_pub_key):
return {
'token_id': token,
@ -195,6 +227,7 @@ def host_request(token, host=host_id, pub_key=host_pub_key):
'key.pub': pub_key
}
def vendordata_request(auth, host):
return {
'instance-id': host,
@ -202,6 +235,7 @@ def vendordata_request(auth, host):
'hostname': 'mytest.testing'
}
def test_post_vendordata_bad_uuid(client):
for key in ['instance-id', 'project-id']:
body = vendordata_request(auth_id, host_id)
@ -212,6 +246,7 @@ def test_post_vendordata_bad_uuid(client):
)
assert response.status == falcon.HTTP_BAD_REQUEST
@pytest.mark.dependency(depends=['test_post_authority'])
def test_post_novavendordata(client):
req = vendordata_request(auth_id, random_uuid())
@ -231,6 +266,7 @@ def test_post_novavendordata(client):
assert 'principals' in vendordata
assert vendordata['principals'] == 'admin'
def test_post_token_bad_uuid(client):
for key in ['auth_id', 'host_id']:
body = token_request()
@ -241,6 +277,7 @@ def test_post_token_bad_uuid(client):
)
assert response.status == falcon.HTTP_BAD_REQUEST
def test_post_host_bad_uuid(client):
for key in ['token_id', 'host_id']:
body = host_request(random_uuid())
@ -251,6 +288,7 @@ def test_post_host_bad_uuid(client):
)
assert response.status == falcon.HTTP_BAD_REQUEST
@pytest.mark.dependency(depends=['test_post_authority'])
def test_post_token_and_host(client):
token = token_request()
@ -327,6 +365,7 @@ def test_stress_post_token_and_host(client):
assert location[3] == fingerprint
assert time.time() - start < 5
@pytest.mark.dependency(depends=['test_post_token_and_host'])
def test_post_token_same_host_id(client):
# Posting with the same host ID should return the same token
@ -342,6 +381,7 @@ def test_post_token_same_host_id(client):
# The token id should be the same as that from the previous test.
assert token_id == location_path[-1]
@pytest.mark.dependency(depends=['test_post_token_and_host'])
def test_get_host(client):
response = client.simulate_get('/hostcerts/' + host_id + '/' + host_fingerprint)
@ -355,14 +395,17 @@ def test_get_host(client):
assert body['fingerprint'] == host_fingerprint
assert body['auth_id'] == auth_id
def test_get_host_doesnt_exist(client):
response = client.simulate_get('/hostcerts/' + random_uuid() + '/' + host_fingerprint)
assert response.status == falcon.HTTP_NOT_FOUND
def test_get_host_with_bad_uuid(client):
response = client.simulate_get('/hostcerts/foobar/' + host_fingerprint)
assert response.status == falcon.HTTP_BAD_REQUEST
def test_post_token_unknown_auth(client):
token = token_request(auth=random_uuid())
response = client.simulate_post(
@ -371,6 +414,7 @@ def test_post_token_unknown_auth(client):
)
assert response.status == falcon.HTTP_NOT_FOUND
@pytest.mark.dependency(depends=['test_post_authority'])
def test_post_host_with_bogus_token(client):
host = host_request(random_uuid(), random_uuid())
@ -380,6 +424,7 @@ def test_post_host_with_bogus_token(client):
)
assert response.status == falcon.HTTP_NOT_FOUND
@pytest.mark.dependency(depends=['test_post_token_and_host'])
def test_post_host_with_wrong_host_id(client):
# Get a new token for the same host_id as the base test.
@ -403,6 +448,7 @@ def test_post_host_with_wrong_host_id(client):
)
assert response.status == falcon.HTTP_CONFLICT
@pytest.mark.dependency(depends=['test_post_token_and_host'])
def test_post_host_different_public_key_fails(client):
# Use the same token compared to the test this depends on.
@ -425,6 +471,7 @@ def test_post_host_different_public_key_fails(client):
)
assert response.status == falcon.HTTP_CONFLICT
@pytest.mark.dependency(depends=['test_post_token_and_host'])
def test_post_host_with_used_token(client):
# Re-use the token from the test this depends on.

View File

@ -1,10 +1,24 @@
# 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.
import os
import subprocess
import uuid
def random_uuid():
return str(uuid.uuid4())
def generateCert(auth_key, entity_key, hostname=None, principals='root'):
# Temporarily write the authority private key and entity public key to files
prefix = uuid.uuid4().hex