magnum/magnum/api/controllers/v1/certificate.py

170 lines
6.2 KiB
Python

# Copyright 2015 NEC Corporation. All rights reserved.
#
# 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_utils import timeutils
import pecan
import wsme
from wsme import types as wtypes
from magnum.api.controllers import base
from magnum.api.controllers import link
from magnum.api.controllers.v1 import types
from magnum.api import expose
from magnum.api import utils as api_utils
from magnum.common import exception
from magnum.common import policy
from magnum import objects
class Certificate(base.APIBase):
"""API representation of a certificate.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of a
certificate.
"""
_cluster_uuid = None
"""uuid or logical name of cluster"""
_cluster = None
def _get_cluster_uuid(self):
return self._cluster_uuid
def _set_cluster_uuid(self, value):
if value and self._cluster_uuid != value:
try:
self._cluster = api_utils.get_resource('Bay', value)
self._cluster_uuid = self._cluster.uuid
except exception.ClusterNotFound as e:
# Change error code because 404 (NotFound) is inappropriate
# response for a POST request to create a Bay
e.code = 400 # BadRequest
raise
elif value == wtypes.Unset:
self._cluster_uuid = wtypes.Unset
bay_uuid = wsme.wsproperty(wtypes.text, _get_cluster_uuid,
_set_cluster_uuid)
"""The bay UUID or id"""
cluster_uuid = wsme.wsproperty(wtypes.text, _get_cluster_uuid,
_set_cluster_uuid)
"""The cluster UUID or id"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated certificate links"""
csr = wtypes.StringType(min_length=1)
""""The Certificate Signing Request"""
pem = wtypes.StringType()
""""The Signed Certificate"""
def __init__(self, **kwargs):
super(Certificate, self).__init__()
self.fields = []
for field in objects.Certificate.fields:
# Skip fields we do not expose.
if not hasattr(self, field):
continue
self.fields.append(field)
setattr(self, field, kwargs.get(field, wtypes.Unset))
# set the attribute for cluster_uuid
self.fields.append('cluster_uuid')
if 'cluster_uuid' in kwargs.keys():
setattr(self, 'cluster_uuid', kwargs.get('cluster_uuid',
wtypes.Unset))
def get_cluster(self):
if not self._cluster:
self._cluster = api_utils.get_resource('Bay', self.cluster_uuid)
return self._cluster
@staticmethod
def _convert_with_links(certificate, url, expand=True):
if not expand:
certificate.unset_fields_except(['bay_uuid', 'cluster_uuid',
'csr', 'pem'])
certificate.links = [link.Link.make_link('self', url,
'certificates',
certificate.bay_uuid),
link.Link.make_link('bookmark', url,
'certificates',
certificate.bay_uuid,
bookmark=True)]
return certificate
@classmethod
def convert_with_links(cls, rpc_cert, expand=True):
cert = Certificate(**rpc_cert.as_dict())
return cls._convert_with_links(cert,
pecan.request.host_url, expand)
@classmethod
def sample(cls, expand=True):
sample = cls(bay_uuid='7ae81bb3-dec3-4289-8d6c-da80bd8001ae',
cluster_uuid='7ae81bb3-dec3-4289-8d6c-da80bd8001ae',
created_at=timeutils.utcnow(),
csr='AAA....AAA')
return cls._convert_with_links(sample, 'http://localhost:9511', expand)
class CertificateController(base.Controller):
"""REST controller for Certificate."""
def __init__(self):
super(CertificateController, self).__init__()
_custom_actions = {
'detail': ['GET'],
}
@expose.expose(Certificate, types.uuid_or_name)
def get_one(self, cluster_ident):
"""Retrieve CA information about the given cluster.
:param cluster_ident: UUID of a cluster or
logical name of the cluster.
"""
context = pecan.request.context
cluster = api_utils.get_resource('Bay', cluster_ident)
policy.enforce(context, 'certificate:get', cluster,
action='certificate:get')
certificate = pecan.request.rpcapi.get_ca_certificate(cluster)
return Certificate.convert_with_links(certificate)
@expose.expose(Certificate, body=Certificate, status_code=201)
def post(self, certificate):
"""Sign a new certificate by the CA.
:param certificate: a certificate within the request body.
"""
context = pecan.request.context
cluster = certificate.get_cluster()
policy.enforce(context, 'certificate:create', cluster,
action='certificate:create')
certificate_dict = certificate.as_dict()
certificate_dict['project_id'] = context.project_id
certificate_dict['user_id'] = context.user_id
cert_obj = objects.Certificate(context, **certificate_dict)
new_cert = pecan.request.rpcapi.sign_certificate(cluster,
cert_obj)
return Certificate.convert_with_links(new_cert)