diff --git a/barbican/api/resources.py b/barbican/api/resources.py index 0d45b019f..e9093e28d 100644 --- a/barbican/api/resources.py +++ b/barbican/api/resources.py @@ -4,8 +4,9 @@ import logging from barbican.version import __version__ from barbican.api import ApiResource, load_body, abort -from barbican.model.models import Tenant +from barbican.model.models import Tenant, States from barbican.model.repositories import TenantRepo +from barbican.queue.resources import QueueResource, StartCSRMessage from barbican.common import config LOG = logging.getLogger(__name__) @@ -54,9 +55,11 @@ class TenantsResource(ApiResource): if tenant: abort(falcon.HTTP_400, 'Tenant with username {0} ' 'already exists'.format(username)) + # TBD: Encrypte fields new_tenant = Tenant() new_tenant.username = username + new_tenant.status = States.ACTIVE self.repo.create_from(new_tenant) print '...post create from' @@ -91,9 +94,10 @@ class TenantResource(ApiResource): class CSRsResource(ApiResource): """Handles CSR (SSL certificate request) creation and lists requests""" - def __init__(self, csr_repo=None): + def __init__(self, csr_repo=None, queue_resource=None): LOG.debug('Creating CSRsResource') self.repo = csr_repo or CSRRepo() + self.queue = queue_resource or QueueResource() def on_post(self, req, resp, tenent_id): body = load_body(req) @@ -110,6 +114,8 @@ class CSRsResource(ApiResource): # abort(falcon.HTTP_400, 'Tenant with username {0} ' # 'already exists'.format(username)) + # TBD: Encrypt fields + new_csr = CSR() new_csr.requestor = requestor self.repo.create_from(new_csr) @@ -117,10 +123,13 @@ class CSRsResource(ApiResource): # TBD: Remove: print '...post create from' - resp.status = falcon.HTTP_201 - resp.set_header('Location', '/{0}'.format(new_csr.id)) + # Send to workers to process. + self.queue.send(StartCSRMessage(new_csr.id)) + + resp.status = falcon.HTTP_202 + resp.set_header('Location', '/{0}/csrs/{1}'.format(tenent_id, new_csr.id)) # TBD: Generate URL... - url = 'http://localhost:8080:/csrs/%s' % new_csr.id + url = 'http://localhost:8080:/%s/csrs/%s' % (tenent_id, new_csr.id) resp.body = json.dumps({'ref': url}) @@ -134,6 +143,7 @@ class CSRResource(ApiResource): csr = self.repo.get(entity_id=csr_id) resp.status = falcon.HTTP_200 + resp.body = json.dumps(csr.to_dict_fields(), default=json_handler) def on_delete(self, req, resp, tenent_id, csr_id): diff --git a/barbican/model/models.py b/barbican/model/models.py index caef05b9e..7bc886552 100644 --- a/barbican/model/models.py +++ b/barbican/model/models.py @@ -30,6 +30,10 @@ from barbican.openstack.common import uuidutils BASE = declarative_base() +# Allowed entity states +class States(object): + PENDING = 'PENDING' + ACTIVE = 'ACTIVE' @compiles(BigInteger, 'sqlite') def compile_big_int_sqlite(type_, compiler, **kw): @@ -52,6 +56,8 @@ class ModelBase(object): deleted_at = Column(DateTime) deleted = Column(Boolean, nullable=False, default=False) + status = Column(String(20), nullable=False, default=States.PENDING) + def save(self, session=None): """Save this object""" # import api here to prevent circular dependency problem @@ -106,7 +112,8 @@ class ModelBase(object): """Returns a dictionary of just the db fields of this entity.""" dict_fields = {'id':self.id, 'created':self.created_at, - 'updated':self.updated_at} + 'updated':self.updated_at, + 'status':self.status} if self.deleted_at: dict_fields['deleted'] = self.deleted_at if self.deleted: @@ -146,11 +153,15 @@ class CSR(BASE, ModelBase): __tablename__ = 'csrs' + tenant_id = Column(String(36), ForeignKey('tenants.id'), + nullable=False) + tenant = relationship(Tenant, backref=backref('csrs')) + requestor = Column(String(255)) def _do_extra_dict_fields(self): """Sub-class hook method: return dict of fields.""" - return {'requestor':self.requestor} + return {'requestor':self.requestor, 'tenant_id':self.tenant_id} class Certificate(BASE, ModelBase): @@ -161,12 +172,23 @@ class Certificate(BASE, ModelBase): __tablename__ = 'certificates' - private_key = Column(String(255)) - public_key = Column(String(255)) + tenant_id = Column(String(36), ForeignKey('tenants.id'), + nullable=False) + tenant = relationship(Tenant, backref=backref('certificates')) + + csr_id = Column(String(36), ForeignKey('csrs.id'), + nullable=False) + csr= relationship(CSR, backref=backref('certificates')) + + private_key = Column(Text) + public_key = Column(Text) def _do_extra_dict_fields(self): """Sub-class hook method: return dict of fields.""" - return {'private_key':self.private_key, 'public_key':self.public_key} + return {'private_key':self.private_key, + 'public_key':self.public_key, + 'tenant_id':self.tenant_id, + 'csr_id':self.tenant_id} # Keep this tuple synchronized with the models in the file diff --git a/barbican/queue/__init__.py b/barbican/queue/__init__.py new file mode 100644 index 000000000..5955c4df0 --- /dev/null +++ b/barbican/queue/__init__.py @@ -0,0 +1,29 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# 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. + +""" +Queue objects for Cloudkeep's Barbican +""" + +from oslo.config import cfg +from barbican.common import config + +queue_opts = [ + cfg.StrOpt('queue_api', default='barbican.queue.simple', + help=_('Python module path of queue implementation API')), +] + +CONF = cfg.CONF +CONF.register_opts(queue_opts, group='queue') diff --git a/barbican/queue/resources.py b/barbican/queue/resources.py new file mode 100644 index 000000000..513d724ef --- /dev/null +++ b/barbican/queue/resources.py @@ -0,0 +1,43 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# 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. + +""" +Queue Resources related objects and functions. +""" +from oslo.config import cfg + + +CONF = cfg.CONF + +def get_queue_api(): + return importutils.import_module(CONF.queue_api) + + +class StartCSRMessage(object): + """Message to start the CSR process""" + + def __init__(self, csr_id): + self.csr_id = csr_id + + +class QueueResource(object): + """Handles Queue related requests""" + + def __init__(self, queue_api=None): + self.api = queue_api or get_queue_api() + + def send(self, message): + self.api.send(message) + \ No newline at end of file diff --git a/barbican/queue/simple.py b/barbican/queue/simple.py new file mode 100644 index 000000000..d53393dad --- /dev/null +++ b/barbican/queue/simple.py @@ -0,0 +1,24 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# 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. + +""" +Simple Queue API implementation. +""" +from barbican.worker.resources import WorkerResource + +def send(message): + """Handle the specified message by simply passing through to the Worker Resource.""" + worker = WorkerResource() + worker.receive(message) \ No newline at end of file diff --git a/barbican/worker/__init__.py b/barbican/worker/__init__.py new file mode 100644 index 000000000..fd5b12de5 --- /dev/null +++ b/barbican/worker/__init__.py @@ -0,0 +1,29 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# 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. + +""" +Worker objects for Cloudkeep's Barbican +""" + +from oslo.config import cfg +from barbican.common import config + +worker_opts = [ + cfg.StrOpt('worker_api', default='barbican.worker.simple', + help=_('Python module path of worker implementation API')), +] + +CONF = cfg.CONF +CONF.register_opts(worker_opts, group='worker') diff --git a/barbican/worker/resources.py b/barbican/worker/resources.py new file mode 100644 index 000000000..5e73515ae --- /dev/null +++ b/barbican/worker/resources.py @@ -0,0 +1,35 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# 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. + +""" +Queue Resources related objects and functions. +""" +from oslo.config import cfg + + +CONF = cfg.CONF + +def get_worker_api(): + return importutils.import_module(CONF.worker_api) + +class WorkerResource(object): + """Handles Queue related requests""" + + def __init__(self, worker_api=None): + self.api = worker_api or get_worker_api() + + def receive(self, message): + self.api.process(message) + diff --git a/barbican/worker/simple.py b/barbican/worker/simple.py new file mode 100644 index 000000000..79f11ff92 --- /dev/null +++ b/barbican/worker/simple.py @@ -0,0 +1,29 @@ +# Copyright (c) 2013 Rackspace, Inc. +# +# 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. + +""" +Simple Worker API implementation. +""" +from barbican.queue.resources import StartCSRMessage + +class StartCSRProcessor(object): + print "Processing CSR with ID = ", csr_id + +PROCESSES = {StartCSRMessage : StartCSRProcessor()} + +def process(message): + """Handle the specified message but simply passing through to the Worker Resource.""" + processor = PROCESSES[message.__class__] + processor.process(message) \ No newline at end of file