317 lines
11 KiB
Python
317 lines
11 KiB
Python
# Copyright (c) 2016 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.
|
|
|
|
import json
|
|
|
|
from cassandra import query
|
|
from oslo_log import log
|
|
from six.moves import filterfalse
|
|
|
|
from poppy.model import ssl_certificate
|
|
from poppy.storage import base
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
CQL_CREATE_CERT = '''
|
|
INSERT INTO certificate_info (project_id,
|
|
flavor_id,
|
|
cert_type,
|
|
domain_name,
|
|
cert_details
|
|
)
|
|
VALUES (%(project_id)s,
|
|
%(flavor_id)s,
|
|
%(cert_type)s,
|
|
%(domain_name)s,
|
|
%(cert_details)s)
|
|
'''
|
|
|
|
CQL_SEARCH_CERT_BY_DOMAIN = '''
|
|
SELECT project_id,
|
|
flavor_id,
|
|
cert_type,
|
|
domain_name,
|
|
cert_details
|
|
FROM certificate_info
|
|
WHERE domain_name = %(domain_name)s
|
|
'''
|
|
|
|
CQL_GET_CERTS_BY_STATUS = '''
|
|
SELECT domain_name
|
|
FROM cert_status WHERE status = %(status)s
|
|
'''
|
|
|
|
CQL_DELETE_CERT = '''
|
|
DELETE FROM certificate_info
|
|
WHERE domain_name = %(domain_name)s
|
|
'''
|
|
|
|
CQL_DELETE_CERT_STATUS = '''
|
|
DELETE FROM cert_status
|
|
WHERE domain_name = %(domain_name)s
|
|
'''
|
|
|
|
|
|
CQL_INSERT_CERT_STATUS = '''
|
|
INSERT INTO cert_status (domain_name,
|
|
status
|
|
)
|
|
VALUES (%(domain_name)s,
|
|
%(status)s)
|
|
'''
|
|
|
|
CQL_UPDATE_CERT_DETAILS = '''
|
|
UPDATE certificate_info
|
|
set cert_details = %(cert_details)s
|
|
WHERE domain_name = %(domain_name)s
|
|
IF cert_type = %(cert_type)s AND flavor_id = %(flavor_id)s
|
|
'''
|
|
|
|
|
|
class CertificatesController(base.CertificatesController):
|
|
|
|
"""Certificates Controller."""
|
|
|
|
@property
|
|
def session(self):
|
|
"""Get session.
|
|
|
|
:returns session
|
|
"""
|
|
return self._driver.database
|
|
|
|
def create_certificate(self, project_id, cert_obj):
|
|
if self.cert_already_exist(domain_name=cert_obj.domain_name,
|
|
comparing_cert_type=cert_obj.cert_type,
|
|
comparing_flavor_id=cert_obj.flavor_id,
|
|
comparing_project_id=project_id):
|
|
raise ValueError('Certificate already exists '
|
|
'for {0} '.format(cert_obj.domain_name))
|
|
|
|
args = {
|
|
'project_id': project_id,
|
|
'flavor_id': cert_obj.flavor_id,
|
|
'cert_type': cert_obj.cert_type,
|
|
'domain_name': cert_obj.domain_name,
|
|
# when create the cert, cert domain has not been assigned yet
|
|
# In future we can tweak the logic to assign cert_domain
|
|
# 'cert_domain': '',
|
|
'cert_details': cert_obj.cert_details
|
|
}
|
|
stmt = query.SimpleStatement(
|
|
CQL_CREATE_CERT,
|
|
consistency_level=self._driver.consistency_level)
|
|
self.session.execute(stmt, args)
|
|
|
|
cert_status = None
|
|
try:
|
|
provider_status = json.loads(
|
|
list(cert_obj.cert_details.values())[0]
|
|
)
|
|
cert_status = provider_status['extra_info']['status']
|
|
except (IndexError, KeyError, ValueError) as e:
|
|
LOG.warning(
|
|
"Create certificate missing extra info "
|
|
"status {0}: Error {1}. "
|
|
"Using 'create_in_progress' instead. ".format(
|
|
cert_obj.cert_details, e))
|
|
cert_status = 'create_in_progress'
|
|
finally:
|
|
# insert/update for cassandra
|
|
self.insert_cert_status(cert_obj.domain_name, cert_status)
|
|
|
|
def delete_certificate(self, project_id, domain_name, cert_type):
|
|
args = {
|
|
'domain_name': domain_name.lower()
|
|
}
|
|
|
|
stmt = query.SimpleStatement(
|
|
CQL_SEARCH_CERT_BY_DOMAIN,
|
|
consistency_level=self._driver.consistency_level)
|
|
result_set = self.session.execute(stmt, args)
|
|
complete_results = list(result_set)
|
|
if complete_results:
|
|
for r in complete_results:
|
|
r_project_id = str(r.get('project_id'))
|
|
r_cert_type = str(r.get('cert_type'))
|
|
if r_project_id == str(project_id) and \
|
|
r_cert_type == str(cert_type):
|
|
args = {
|
|
'domain_name': str(r.get('domain_name'))
|
|
}
|
|
stmt = query.SimpleStatement(
|
|
CQL_DELETE_CERT,
|
|
consistency_level=self._driver.consistency_level)
|
|
self.session.execute(stmt, args)
|
|
stmt = query.SimpleStatement(
|
|
CQL_DELETE_CERT_STATUS,
|
|
consistency_level=self._driver.consistency_level)
|
|
self.session.execute(stmt, args)
|
|
else:
|
|
raise ValueError(
|
|
"No certificate found for: {0},"
|
|
"type: {1}".format(domain_name, cert_type))
|
|
|
|
def update_certificate(self, domain_name, cert_type, flavor_id,
|
|
cert_details):
|
|
|
|
args = {
|
|
'domain_name': domain_name,
|
|
'cert_type': cert_type,
|
|
'flavor_id': flavor_id,
|
|
'cert_details': cert_details
|
|
}
|
|
stmt = query.SimpleStatement(
|
|
CQL_UPDATE_CERT_DETAILS,
|
|
consistency_level=self._driver.consistency_level)
|
|
self.session.execute(stmt, args)
|
|
|
|
try:
|
|
provider_status = json.loads(list(cert_details.values())[0])
|
|
cert_status = provider_status['extra_info']['status']
|
|
self.insert_cert_status(domain_name, cert_status)
|
|
except (IndexError, KeyError, ValueError) as e:
|
|
# certs already existing in DB should have all
|
|
# the necessary fields
|
|
LOG.error(
|
|
"Unable to update cert_status because certificate "
|
|
"details are in an inconsistent "
|
|
"state: {0}: {1}".format(cert_details, e))
|
|
|
|
def insert_cert_status(self, domain_name, cert_status):
|
|
cert_args = {
|
|
'domain_name': domain_name,
|
|
'status': cert_status
|
|
}
|
|
stmt = query.SimpleStatement(
|
|
CQL_INSERT_CERT_STATUS,
|
|
consistency_level=self._driver.consistency_level)
|
|
self.session.execute(stmt, cert_args)
|
|
|
|
def get_certs_by_status(self, status):
|
|
|
|
LOG.info("Getting domains which have "
|
|
"certificate in status : {0}".format(status))
|
|
args = {
|
|
'status': status
|
|
}
|
|
stmt = query.SimpleStatement(
|
|
CQL_GET_CERTS_BY_STATUS,
|
|
consistency_level=self._driver.consistency_level)
|
|
resultset = self.session.execute(stmt, args)
|
|
complete_results = list(resultset)
|
|
|
|
return complete_results
|
|
|
|
def get_certs_by_domain(self, domain_name, project_id=None,
|
|
flavor_id=None,
|
|
cert_type=None):
|
|
|
|
LOG.info("Check if cert on '{0}' exists".format(domain_name))
|
|
args = {
|
|
'domain_name': domain_name.lower()
|
|
}
|
|
stmt = query.SimpleStatement(
|
|
CQL_SEARCH_CERT_BY_DOMAIN,
|
|
consistency_level=self._driver.consistency_level)
|
|
resultset = self.session.execute(stmt, args)
|
|
complete_results = list(resultset)
|
|
certs = []
|
|
if complete_results:
|
|
for r in complete_results:
|
|
r_project_id = str(r.get('project_id'))
|
|
r_flavor_id = str(r.get('flavor_id'))
|
|
r_cert_type = str(r.get('cert_type'))
|
|
r_cert_details = {}
|
|
# in case cert_details is None
|
|
cert_details = r.get('cert_details', {}) or {}
|
|
# Need to convert cassandra dict into real dict
|
|
# And the value of cert_details is a string dict
|
|
for key in cert_details:
|
|
r_cert_details[key] = json.loads(cert_details[key])
|
|
LOG.info(
|
|
"Certificate for domain: {0} with flavor_id: {1}, "
|
|
"cert_details : {2} and cert_type: {3} present "
|
|
"on project_id: {4}".format(
|
|
domain_name,
|
|
r_flavor_id,
|
|
r_cert_details,
|
|
r_cert_type,
|
|
r_project_id
|
|
)
|
|
)
|
|
ssl_cert = ssl_certificate.SSLCertificate(
|
|
domain_name=domain_name,
|
|
flavor_id=r_flavor_id,
|
|
cert_details=r_cert_details,
|
|
cert_type=r_cert_type,
|
|
project_id=r_project_id
|
|
)
|
|
|
|
certs.append(ssl_cert)
|
|
|
|
non_none_attrs_gen = filterfalse(
|
|
lambda x: list(x.values())[0] is None, [{'project_id': project_id},
|
|
{'flavor_id': flavor_id},
|
|
{'cert_type': cert_type}])
|
|
non_none_attrs_list = list(non_none_attrs_gen)
|
|
non_none_attrs_dict = {}
|
|
|
|
if non_none_attrs_list:
|
|
for attr in non_none_attrs_list:
|
|
non_none_attrs_dict.update(attr)
|
|
|
|
def argfilter(certificate):
|
|
all_conditions = True
|
|
if non_none_attrs_dict:
|
|
for k, v in non_none_attrs_dict.items():
|
|
if getattr(certificate, k) != v:
|
|
all_conditions = False
|
|
|
|
return all_conditions
|
|
|
|
total_certs = [cert for cert in certs if argfilter(cert)]
|
|
|
|
if len(total_certs) == 1:
|
|
return total_certs[0]
|
|
else:
|
|
return total_certs
|
|
|
|
def cert_already_exist(self, domain_name, comparing_cert_type,
|
|
comparing_flavor_id, comparing_project_id):
|
|
"""cert_already_exist
|
|
|
|
Check if a cert with this domain name and type has already been
|
|
created, or if the domain has been taken by other customers
|
|
|
|
:param domain_name
|
|
:param comparing_cert_type
|
|
:param comparing_flavor_id
|
|
:param comparing_project_id
|
|
|
|
:returns Boolean if the cert with same type exists with another user.
|
|
"""
|
|
cert = self.get_certs_by_domain(
|
|
domain_name=domain_name,
|
|
cert_type=comparing_cert_type,
|
|
flavor_id=comparing_flavor_id
|
|
)
|
|
|
|
if cert:
|
|
return True
|
|
else:
|
|
return False
|