Merge "Drop all remaining logics for certificate resources"

This commit is contained in:
Zuul 2024-03-08 16:18:59 +00:00 committed by Gerrit Code Review
commit b6edfda344
27 changed files with 12 additions and 6228 deletions

View File

@ -15,10 +15,10 @@ storage keys that are stored either in a software NSS database or in an HSM. It
can serve as a secret store for barbican, and interacts with barbican core through can serve as a secret store for barbican, and interacts with barbican core through
the Dogtag KRA plugin. the Dogtag KRA plugin.
In this guide, we will provide instructions on how to set up a basic Dogtag instance In this guide, we will provide instructions on how to set up a basic Dogtag
containing a CA and a KRA, and how to configure barbican to use this instance for a instance containing a CA and a KRA, and how to configure barbican to use this
secret store and a certificate plugin. Much more detail about Dogtag, its deployment instance for a secret store. Much more detail about Dogtag, its deployment
options and its administration are available in the `RHCS documentation options and its administration are available in the `RHCS documentation
<https://access.redhat.com/documentation/en-US/Red_Hat_Certificate_System>`_. <https://access.redhat.com/documentation/en-US/Red_Hat_Certificate_System>`_.
**Note:** The code below is taken from the devstack Barbican-Dogtag gate job. You can **Note:** The code below is taken from the devstack Barbican-Dogtag gate job. You can
@ -166,8 +166,8 @@ created with trusted agent credentials.
chown $USER $BARBICAN_CONF_DIR/kra_admin_cert.pem chown $USER $BARBICAN_CONF_DIR/kra_admin_cert.pem
The barbican config file (/etc/barbican/barbican.conf) needs to be modified. The barbican config file (/etc/barbican/barbican.conf) needs to be modified.
The modifications below set the Dogtag plugins as the only enabled secret store and The modifications below set the Dogtag plugins as the only enabled secret store.
certificate plugins. Be sure to restart barbican once these changes are made. Makee sure to restart barbican once these changes are made.
Note that the actual hostname of the machine should be used in the script (rather Note that the actual hostname of the machine should be used in the script (rather
than localhost) because the hostname is used in the subject name for the SSL than localhost) because the hostname is used in the subject name for the SSL
@ -189,10 +189,6 @@ server certificate for the CA.
namespace = barbican.secretstore.plugin namespace = barbican.secretstore.plugin
enabled_secretstore_plugins = dogtag_crypto enabled_secretstore_plugins = dogtag_crypto
[certificate]
namespace = barbican.certificate.plugin
enabled_certificate_plugins = dogtag
Testing the Setup Testing the Setup
***************** *****************

View File

@ -52,7 +52,6 @@ class States(object):
class OrderType(object): class OrderType(object):
KEY = 'key' KEY = 'key'
ASYMMETRIC = 'asymmetric' ASYMMETRIC = 'asymmetric'
CERTIFICATE = 'certificate'
@classmethod @classmethod
def is_valid(cls, order_type): def is_valid(cls, order_type):

View File

@ -14,30 +14,18 @@
# limitations under the License. # limitations under the License.
import base64 import base64
import copy
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
import datetime
import os import os
from oslo_utils import uuidutils from oslo_utils import uuidutils
import time import time
import pki import pki
subcas_available = True
try:
import pki.authority as authority
import pki.feature as feature
except ImportError:
subcas_available = False
import pki.cert
import pki.client import pki.client
import pki.crypto as cryptoutil import pki.crypto as cryptoutil
import pki.key as key import pki.key as key
import pki.kra import pki.kra
import pki.profile import pki.profile
from requests import exceptions as request_exceptions
from barbican.common import exception from barbican.common import exception
from barbican.common import utils from barbican.common import utils
@ -47,7 +35,6 @@ from barbican import i18n as u
# do not need to import every dogtag requirement to generate the # do not need to import every dogtag requirement to generate the
# sample config # sample config
import barbican.plugin.dogtag_config_opts # noqa import barbican.plugin.dogtag_config_opts # noqa
import barbican.plugin.interface.certificate_manager as cm
import barbican.plugin.interface.secret_store as sstore import barbican.plugin.interface.secret_store as sstore
# reuse the conf object to not call config.new_config() twice # reuse the conf object to not call config.new_config() twice
@ -618,734 +605,3 @@ class DogtagKRAPlugin(sstore.SecretStoreBase):
) )
return twsk return twsk
def _catch_request_exception(ca_related_function):
def _catch_ca_unavailable(self, *args, **kwargs):
try:
return ca_related_function(self, *args, **kwargs)
except request_exceptions.RequestException:
return cm.ResultDTO(
cm.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST)
return _catch_ca_unavailable
def _catch_enrollment_exceptions(ca_related_function):
def _catch_enrollment_exception(self, *args, **kwargs):
try:
return ca_related_function(self, *args, **kwargs)
except pki.BadRequestException as e:
return cm.ResultDTO(
cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
status_message=e.message)
except pki.PKIException as e:
raise cm.CertificateGeneralException(
u._("Exception thrown by enroll_cert: {message}").format(
message=e.message))
return _catch_enrollment_exception
def _catch_subca_creation_exceptions(ca_related_function):
def _catch_subca_exception(self, *args, **kwargs):
try:
return ca_related_function(self, *args, **kwargs)
except pki.BadRequestException as e:
raise exception.BadSubCACreationRequest(reason=e.message)
except pki.PKIException as e:
raise exception.SubCACreationErrors(reason=e.message)
except request_exceptions.RequestException:
raise exception.SubCACreationErrors(
reason="Unable to connect to CA")
return _catch_subca_exception
def _catch_subca_deletion_exceptions(ca_related_function):
def _catch_subca_exception(self, *args, **kwargs):
try:
return ca_related_function(self, *args, **kwargs)
except pki.ResourceNotFoundException:
LOG.warning("Sub-CA already deleted")
pass
except pki.PKIException as e:
raise exception.SubCADeletionErrors(reason=e.message)
except request_exceptions.RequestException:
raise exception.SubCACreationErrors(
reason="Unable to connect to CA")
return _catch_subca_exception
class DogtagCAPlugin(cm.CertificatePluginBase):
"""Implementation of the cert plugin with Dogtag CA as the backend."""
# order_metadata fields
PROFILE_ID = "profile_id"
# plugin_metadata fields
REQUEST_ID = "request_id"
def __init__(self, conf=CONF):
"""Constructor - create the cert clients."""
connection = create_connection(conf, 'ca')
self.certclient = pki.cert.CertClient(connection)
self.simple_cmc_profile = conf.dogtag_plugin.simple_cmc_profile
self.auto_approved_profiles = conf.dogtag_plugin.auto_approved_profiles
self.working_dir = conf.dogtag_plugin.plugin_working_dir
if not os.path.isdir(self.working_dir):
os.mkdir(self.working_dir)
self._expiration = None
self._expiration_delta = conf.dogtag_plugin.ca_expiration_time
self._expiration_data_path = os.path.join(self.working_dir,
"expiration_data.txt")
self._host_aid_path = os.path.join(self.working_dir, "host_aid.txt")
self._host_aid = None
if not os.path.isfile(self._expiration_data_path):
self.expiration = datetime.datetime.utcnow()
global subcas_available
subcas_available = self._are_subcas_enabled_on_backend(connection)
if subcas_available:
self.authority_client = authority.AuthorityClient(connection)
if not os.path.isfile(self._host_aid_path):
self.host_aid = self.get_host_aid()
@property
def expiration(self):
if self._expiration is None:
try:
with open(self._expiration_data_path) as expiration_fh:
self._expiration = datetime.datetime.strptime(
expiration_fh.read(),
"%Y-%m-%d %H:%M:%S.%f"
)
except (ValueError, TypeError):
LOG.warning("Invalid data read from expiration file")
self.expiration = datetime.utcnow()
return self._expiration
@expiration.setter
def expiration(self, val):
with open(self._expiration_data_path, 'w') as expiration_fh:
expiration_fh.write(val.strftime("%Y-%m-%d %H:%M:%S.%f"))
self._expiration = val
@property
def host_aid(self):
if self._host_aid is None:
with open(self._host_aid_path) as host_aid_fh:
self._host_aid = host_aid_fh.read()
return self._host_aid
@host_aid.setter
def host_aid(self, val):
if val is not None:
with open(self._host_aid_path, 'w') as host_aid_fh:
host_aid_fh.write(val)
self._host_aid = val
def _are_subcas_enabled_on_backend(self, connection):
"""Check if subca feature is available
SubCA creation must be supported in both the Dogtag client as well
as on the back-end server. Moreover, it must be enabled on the
backend server. This method sets the subcas_available global variable.
:return: True/False
"""
global subcas_available
if subcas_available:
# subcas are supported in the Dogtag client
try:
feature_client = feature.FeatureClient(connection)
authority_feature = feature_client.get_feature("authority")
if authority_feature.enabled:
LOG.info("Sub-CAs are enabled by Dogtag server")
return True
else:
LOG.info("Sub-CAs are not enabled by Dogtag server")
except (request_exceptions.HTTPError,
pki.ResourceNotFoundException):
LOG.info("Sub-CAs are not supported by Dogtag server")
else:
LOG.info("Sub-CAs are not supported by Dogtag client")
return False
def _get_request_id(self, order_id, plugin_meta, operation):
request_id = plugin_meta.get(self.REQUEST_ID, None)
if not request_id:
raise cm.CertificateGeneralException(
u._(
"{request} not found for {operation} for "
"order_id {order_id}"
).format(
request=self.REQUEST_ID,
operation=operation,
order_id=order_id
)
)
return request_id
@_catch_request_exception
def _get_request(self, request_id):
try:
return self.certclient.get_request(request_id)
except pki.RequestNotFoundException:
return None
@_catch_request_exception
def _get_cert(self, cert_id):
try:
return self.certclient.get_cert(cert_id)
except pki.CertNotFoundException:
return None
def get_default_ca_name(self):
return "Dogtag CA"
def get_default_signing_cert(self):
# TODO(alee) Add code to get the signing cert
return None
def get_default_intermediates(self):
# TODO(alee) Add code to get the cert chain
return None
def check_certificate_status(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Check the status of a certificate request.
:param order_id: ID of the order associated with this request
:param order_meta: order_metadata associated with this order
:param plugin_meta: data populated by previous calls for this order,
in particular the request_id
:param barbican_meta_dto: additional data needed to process order.
:return: cm.ResultDTO
"""
request_id = self._get_request_id(order_id, plugin_meta, "checking")
request = self._get_request(request_id)
if not request:
raise cm.CertificateGeneralException(
u._(
"No request found for request_id {request_id} for "
"order {order_id}"
).format(
request_id=request_id,
order_id=order_id
)
)
request_status = request.request_status
if request_status == pki.cert.CertRequestStatus.REJECTED:
return cm.ResultDTO(
cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
status_message=request.error_message)
elif request_status == pki.cert.CertRequestStatus.CANCELED:
return cm.ResultDTO(
cm.CertificateStatus.REQUEST_CANCELED)
elif request_status == pki.cert.CertRequestStatus.PENDING:
return cm.ResultDTO(
cm.CertificateStatus.WAITING_FOR_CA)
elif request_status == pki.cert.CertRequestStatus.COMPLETE:
# get the cert
cert_id = request.cert_id
if not cert_id:
raise cm.CertificateGeneralException(
u._(
"Request {request_id} reports status_complete, but no "
"cert_id has been returned"
).format(
request_id=request_id
)
)
cert = self._get_cert(cert_id)
if not cert:
raise cm.CertificateGeneralException(
u._("Certificate not found for cert_id: {cert_id}").format(
cert_id=cert_id
)
)
return cm.ResultDTO(
cm.CertificateStatus.CERTIFICATE_GENERATED,
certificate=cert.encoded,
intermediates=cert.pkcs7_cert_chain)
else:
raise cm.CertificateGeneralException(
u._("Invalid request_status returned by CA"))
@_catch_request_exception
def issue_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Issue a certificate request to the Dogtag CA
Call the relevant certificate issuance function depending on the
Barbican defined request type in the order_meta.
:param order_id: ID of the order associated with this request
:param order_meta: dict containing all the inputs for this request.
This includes the request_type.
:param plugin_meta: Used to store data for status check
:param barbican_meta_dto: additional data needed to process order.
:return: cm.ResultDTO
"""
request_type = order_meta.get(
cm.REQUEST_TYPE,
cm.CertificateRequestType.CUSTOM_REQUEST)
jump_table = {
cm.CertificateRequestType.SIMPLE_CMC_REQUEST:
self._issue_simple_cmc_request,
cm.CertificateRequestType.FULL_CMC_REQUEST:
self._issue_full_cmc_request,
cm.CertificateRequestType.STORED_KEY_REQUEST:
self._issue_stored_key_request,
cm.CertificateRequestType.CUSTOM_REQUEST:
self._issue_custom_certificate_request
}
if request_type not in jump_table:
raise DogtagPluginNotSupportedException(u._(
"Dogtag plugin does not support %s request type").format(
request_type))
return jump_table[request_type](order_id, order_meta, plugin_meta,
barbican_meta_dto)
@_catch_enrollment_exceptions
def _issue_simple_cmc_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Issue a simple CMC request to the Dogtag CA.
:param order_id:
:param order_meta:
:param plugin_meta:
:param barbican_meta_dto:
:return: cm.ResultDTO
"""
if barbican_meta_dto.generated_csr is not None:
csr = barbican_meta_dto.generated_csr
else:
# we expect the CSR to be base64 encoded PEM
# Dogtag CA needs it to be unencoded
csr = base64.b64decode(order_meta.get('request_data'))
profile_id = order_meta.get('profile', self.simple_cmc_profile)
inputs = {
'cert_request_type': 'pkcs10',
'cert_request': csr
}
return self._issue_certificate_request(
profile_id, inputs, plugin_meta, barbican_meta_dto)
@_catch_enrollment_exceptions
def _issue_full_cmc_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Issue a full CMC request to the Dogtag CA.
:param order_id:
:param order_meta:
:param plugin_meta:
:param barbican_meta_dto:
:return: cm.ResultDTO
"""
raise DogtagPluginNotSupportedException(u._(
"Dogtag plugin does not support %s request type").format(
cm.CertificateRequestType.FULL_CMC_REQUEST))
@_catch_enrollment_exceptions
def _issue_stored_key_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Issue a simple CMC request to the Dogtag CA.
:param order_id:
:param order_meta:
:param plugin_meta:
:param barbican_meta_dto:
:return: cm.ResultDTO
"""
return self._issue_simple_cmc_request(
order_id,
order_meta,
plugin_meta,
barbican_meta_dto)
@_catch_enrollment_exceptions
def _issue_custom_certificate_request(self, order_id, order_meta,
plugin_meta, barbican_meta_dto):
"""Issue a custom certificate request to Dogtag CA
:param order_id: ID of the order associated with this request
:param order_meta: dict containing all the inputs required for a
particular profile. One of these must be the profile_id.
The exact fields (both optional and mandatory) depend on the
profile, but they will be exposed to the user in a method to
expose syntax. Depending on the profile, only the relevant fields
will be populated in the request. All others will be ignored.
:param plugin_meta: Used to store data for status check.
:param barbican_meta_dto: Extra data to aid in processing.
:return: cm.ResultDTO
"""
profile_id = order_meta.get(self.PROFILE_ID, None)
if not profile_id:
return cm.ResultDTO(
cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
status_message=u._("No profile_id specified"))
# we expect the csr to be base64 encoded PEM data. Dogtag CA expects
# PEM data though so we need to decode it.
updated_meta = copy.deepcopy(order_meta)
if 'cert_request' in updated_meta:
updated_meta['cert_request'] = base64.b64decode(
updated_meta['cert_request'])
return self._issue_certificate_request(
profile_id, updated_meta, plugin_meta, barbican_meta_dto)
def _issue_certificate_request(self, profile_id, inputs, plugin_meta,
barbican_meta_dto):
"""Actually send the cert request to the Dogtag CA
If the profile_id is one of the auto-approved profiles, then use
the convenience enroll_cert() method to create and approve the request
using the Barbican agent cert credentials. If not, then submit the
request and wait for approval by a CA agent on the Dogtag CA.
:param profile_id: enrollment profile
:param inputs: dict of request inputs
:param plugin_meta: Used to store data for status check.
:param barbican_meta_dto: Extra data to aid in processing.
:return: cm.ResultDTO
"""
ca_id = barbican_meta_dto.plugin_ca_id or self.get_default_ca_name()
if profile_id in self.auto_approved_profiles:
if ca_id == self.get_default_ca_name():
results = self.certclient.enroll_cert(profile_id, inputs)
else:
results = self.certclient.enroll_cert(
profile_id, inputs, ca_id)
return self._process_auto_enrollment_results(
results, plugin_meta, barbican_meta_dto)
else:
request = self.certclient.create_enrollment_request(
profile_id, inputs)
if ca_id == self.get_default_ca_name():
results = self.certclient.submit_enrollment_request(request)
else:
results = self.certclient.submit_enrollment_request(
request, ca_id)
return self._process_pending_enrollment_results(
results, plugin_meta, barbican_meta_dto)
def _process_auto_enrollment_results(self, enrollment_results,
plugin_meta, barbican_meta_dto):
"""Process results received from Dogtag CA for auto-enrollment
This processes data from enroll_cert, which submits, approves and
gets the cert issued and returns as a list of CertEnrollment objects.
:param enrollment_results: list of CertEnrollmentResult objects
:param plugin_meta: metadata dict for storing plugin specific data
:param barbican_meta_dto: object containing extra data to help process
the request
:return: cm.ResultDTO
"""
# Although it is possible to create multiple certs in an invocation
# of enroll_cert, Barbican cannot handle this case. Assume
# only once cert and request generated for now.
enrollment_result = enrollment_results[0]
request = enrollment_result.request
if not request:
raise cm.CertificateGeneralException(
u._("No request returned in enrollment_results"))
# store the request_id in the plugin metadata
plugin_meta[self.REQUEST_ID] = request.request_id
cert = enrollment_result.cert
return self._create_dto(request.request_status,
request.request_id,
request.error_message,
cert)
def _process_pending_enrollment_results(self, results, plugin_meta,
barbican_meta_dto):
"""Process results received from Dogtag CA for pending enrollment
This method processes data returned by submit_enrollment_request(),
which creates requests that still need to be approved by an agent.
:param results: CertRequestInfoCollection object
:param plugin_meta: metadata dict for storing plugin specific data
:param barbican_meta_dto: object containing extra data to help process
the request
:return: cm.ResultDTO
"""
# Although it is possible to create multiple certs in an invocation
# of enroll_cert, Barbican cannot handle this case. Assume
# only once cert and request generated for now
cert_request_info = results.cert_request_info_list[0]
status = cert_request_info.request_status
request_id = getattr(cert_request_info, 'request_id', None)
error_message = getattr(cert_request_info, 'error_message', None)
# store the request_id in the plugin metadata
if request_id:
plugin_meta[self.REQUEST_ID] = request_id
return self._create_dto(status, request_id, error_message, None)
def _create_dto(self, request_status, request_id, error_message, cert):
dto = None
if request_status == pki.cert.CertRequestStatus.COMPLETE:
if cert is not None:
# Barbican is expecting base 64 encoded PEM, so we base64
# encode below.
#
# Currently there is an inconsistency in what Dogtag returns
# for certificates and intermediates. For certs, we return
# PEM, whereas for intermediates, we return headerless PEM.
# This is being addressed in Dogtag ticket:
# https://fedorahosted.org/pki/ticket/1374
#
# Until this is addressed, simply add the missing headers
cert_chain = (CERT_HEADER + "\r\n" + cert.pkcs7_cert_chain +
CERT_FOOTER)
dto = cm.ResultDTO(cm.CertificateStatus.CERTIFICATE_GENERATED,
certificate=base64.b64encode(cert.encoded),
intermediates=base64.b64encode(cert_chain))
else:
raise cm.CertificateGeneralException(
u._("request_id {req_id} returns COMPLETE but no cert "
"returned").format(req_id=request_id))
elif request_status == pki.cert.CertRequestStatus.REJECTED:
dto = cm.ResultDTO(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
status_message=error_message)
elif request_status == pki.cert.CertRequestStatus.CANCELED:
dto = cm.ResultDTO(cm.CertificateStatus.REQUEST_CANCELED)
elif request_status == pki.cert.CertRequestStatus.PENDING:
dto = cm.ResultDTO(cm.CertificateStatus.WAITING_FOR_CA)
else:
raise cm.CertificateGeneralException(
u._("Invalid request_status {status} for "
"request_id {request_id}").format(
status=request_status,
request_id=request_id)
)
return dto
def modify_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Modify a certificate request.
Once a certificate request is generated, it cannot be modified.
The only alternative is to cancel the request (if it has not already
completed) and attempt a fresh enrolment. That is what will be
attempted here.
:param order_id: ID for this order
:param order_meta: order metadata. It is assumed that the newly
modified request data will be present here.
:param plugin_meta: data stored on behalf of the plugin for further
operations
:param barbican_meta_dto: additional data needed to process order.
:return: ResultDTO:
"""
result_dto = self.cancel_certificate_request(
order_id, order_meta, plugin_meta, barbican_meta_dto)
if result_dto.status == cm.CertificateStatus.REQUEST_CANCELED:
return self.issue_certificate_request(
order_id, order_meta, plugin_meta, barbican_meta_dto)
elif result_dto.status == cm.CertificateStatus.INVALID_OPERATION:
return cm.ResultDTO(
cm.CertificateStatus.INVALID_OPERATION,
status_message=u._(
"Modify request: unable to cancel: "
"{message}").format(message=result_dto.status_message)
)
else:
# other status (ca_unavailable, client_data_issue)
# return result from cancel operation
return result_dto
@_catch_request_exception
def cancel_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Cancel a certificate request.
:param order_id: ID for the order associated with this request
:param order_meta: order metadata fdr this request
:param plugin_meta: data stored by plugin for further processing.
In particular, the request_id
:param barbican_meta_dto: additional data needed to process order.
:return: cm.ResultDTO:
"""
request_id = self._get_request_id(order_id, plugin_meta, "cancelling")
try:
review_response = self.certclient.review_request(request_id)
self.certclient.cancel_request(request_id, review_response)
return cm.ResultDTO(cm.CertificateStatus.REQUEST_CANCELED)
except pki.RequestNotFoundException:
return cm.ResultDTO(
cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
status_message=u._("no request found for this order"))
except pki.ConflictingOperationException as e:
return cm.ResultDTO(
cm.CertificateStatus.INVALID_OPERATION,
status_message=e.message)
def supports(self, certificate_spec):
if cm.CA_TYPE in certificate_spec:
return certificate_spec[cm.CA_TYPE] == cm.CA_PLUGIN_TYPE_DOGTAG
if cm.CA_PLUGIN_TYPE_SYMANTEC in certificate_spec:
# TODO(alee-3) Handle case where SKI is provided
pass
return True
def supported_request_types(self):
"""Returns the request_types supported by this plugin.
:returns: a list of the Barbican-core defined request_types
supported by this plugin.
"""
return [cm.CertificateRequestType.SIMPLE_CMC_REQUEST,
cm.CertificateRequestType.STORED_KEY_REQUEST,
cm.CertificateRequestType.CUSTOM_REQUEST]
def supports_create_ca(self):
"""Returns if this plugin and the backend CA supports subCAs
:return: True/False
"""
return subcas_available
@_catch_subca_creation_exceptions
def create_ca(self, ca_create_dto):
"""Creates a subordinate CA upon request
:param ca_create_dto:
Data transfer object :class:`CACreateDTO` containing data
required to generate a subordinate CA. This data includes
the subject DN of the new CA signing certificate, a name for
the new CA and a reference to the CA that will issue the new
subordinate CA's signing certificate,
:return: ca_info:
Dictionary containing the data needed to create a
models.CertificateAuthority object
"""
if not subcas_available:
raise exception.SubCAsNotSupported(
"Subordinate CAs are not supported by this Dogtag CA")
parent_ca_id = self._get_correct_ca_id(ca_create_dto.parent_ca_id)
ca_data = authority.AuthorityData(
dn=ca_create_dto.subject_dn,
parent_aid=parent_ca_id,
description=ca_create_dto.name)
new_ca_data = self.authority_client.create_ca(ca_data)
cert = self.authority_client.get_cert(new_ca_data.aid, "PEM")
chain = self.authority_client.get_chain(new_ca_data.aid, "PEM")
return {
cm.INFO_NAME: new_ca_data.description,
cm.INFO_CA_SIGNING_CERT: cert,
cm.INFO_EXPIRATION: self.expiration.isoformat(),
cm.INFO_INTERMEDIATES: chain,
cm.PLUGIN_CA_ID: new_ca_data.aid
}
def _get_correct_ca_id(self, plugin_ca_id):
"""Returns the correct authority id
When the Dogtag plugin updates its CA list, any subcas will
have a plugin_ca_id that matches the authority_id (aid) as
returned from the backend CA.
For migration purposes, though, ie. migrating from a non-subca
environment to a subca one, we want the host CA to keep the same
plugin_ca_id (which is the default_ca_name) so that no disruption
occurs. Therefore, we need to store the host CA's authority ID
(in get_ca_info) and return it here instead.
"""
if plugin_ca_id == self.get_default_ca_name():
return self.host_aid
else:
return plugin_ca_id
@_catch_subca_deletion_exceptions
def delete_ca(self, ca_id):
"""Deletes a subordinate CA
:param ca_id: id for the CA as specified by the plugin
:return: None
"""
if not subcas_available:
raise exception.SubCAsNotSupported(
"Subordinate CAs are not supported by this Dogtag CA")
# ca must be disabled first
self.authority_client.disable_ca(ca_id)
self.authority_client.delete_ca(ca_id)
def get_ca_info(self):
if not subcas_available:
return super(DogtagCAPlugin, self).get_ca_info()
self.expiration = (datetime.datetime.utcnow() + datetime.timedelta(
days=int(self._expiration_delta)))
ret = {}
cas = self.authority_client.list_cas()
for ca_data in cas.ca_list:
if not ca_data.enabled:
continue
cert = self.authority_client.get_cert(ca_data.aid, "PEM")
chain = self.authority_client.get_chain(ca_data.aid, "PEM")
ca_info = {
cm.INFO_NAME: ca_data.description,
cm.INFO_CA_SIGNING_CERT: cert,
cm.INFO_INTERMEDIATES: chain,
cm.INFO_EXPIRATION: self.expiration.isoformat()
}
# handle the migration case. The top level CA should continue
# to work as before
if ca_data.is_host_authority:
ret[self.get_default_ca_name()] = ca_info
self.host_aid = ca_data.aid
else:
ret[ca_data.aid] = ca_info
return ret
def get_host_aid(self):
cas = self.authority_client.list_cas()
for ca_data in cas.ca_list:
if ca_data.is_host_authority:
return ca_data.aid
return None

View File

@ -18,8 +18,6 @@ from oslo_config import cfg
from barbican.common import config from barbican.common import config
from barbican import i18n as u from barbican import i18n as u
import barbican.plugin.interface.certificate_manager as cm
CONF = config.new_config() CONF = config.new_config()
dogtag_plugin_group = cfg.OptGroup(name='dogtag_plugin', dogtag_plugin_group = cfg.OptGroup(name='dogtag_plugin',
@ -40,18 +38,6 @@ dogtag_plugin_opts = [
cfg.StrOpt('nss_password', cfg.StrOpt('nss_password',
help=u._('Password for the NSS certificate databases'), help=u._('Password for the NSS certificate databases'),
secret=True), secret=True),
cfg.StrOpt('simple_cmc_profile',
default='caOtherCert',
help=u._('Profile for simple CMC requests')),
cfg.StrOpt('auto_approved_profiles',
default="caServerCert",
help=u._('List of automatically approved enrollment profiles')),
cfg.IntOpt('ca_expiration_time',
default=cm.CA_INFO_DEFAULT_EXPIRATION_DAYS,
help=u._('Time in days for CA entries to expire')),
cfg.StrOpt('plugin_working_dir',
default='/etc/barbican/dogtag',
help=u._('Working directory for Dogtag plugin')),
cfg.StrOpt('plugin_name', cfg.StrOpt('plugin_name',
help=u._('User friendly plugin name'), help=u._('User friendly plugin name'),
default='Dogtag KRA'), default='Dogtag KRA'),

View File

@ -1,767 +0,0 @@
# Copyright (c) 2013-2014 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.
"""
SSL Certificate resources for Barbican.
The resources here should be generic across all certificate-related
implementations. Hence do not place vendor-specific content in this module.
"""
import abc
import datetime
from oslo_config import cfg
from oslo_utils import encodeutils
from stevedore import named
from barbican.common import config
from barbican.common import exception
import barbican.common.utils as utils
from barbican import i18n as u
from barbican.model import models
from barbican.model import repositories as repos
from barbican.plugin.util import utils as plugin_utils
LOG = utils.getLogger(__name__)
CONF = config.new_config()
# Configuration for certificate processing plugins:
DEFAULT_PLUGIN_NAMESPACE = 'barbican.certificate.plugin'
DEFAULT_PLUGINS = ['simple_certificate']
cert_opt_group = cfg.OptGroup(name='certificate',
title='Certificate Plugin Options')
cert_opts = [
cfg.StrOpt('namespace',
default=DEFAULT_PLUGIN_NAMESPACE,
help=u._('Extension namespace to search for plugins.')
),
cfg.MultiStrOpt('enabled_certificate_plugins',
default=DEFAULT_PLUGINS,
help=u._('List of certificate plugins to load.')
)
]
CONF.register_group(cert_opt_group)
CONF.register_opts(cert_opts, group=cert_opt_group)
config.parse_args(CONF)
def list_opts():
yield cert_opt_group, cert_opts
yield cert_event_opt_group, cert_event_opts
# Configuration for certificate eventing plugins:
DEFAULT_EVENT_PLUGIN_NAMESPACE = 'barbican.certificate.event.plugin'
DEFAULT_EVENT_PLUGINS = ['simple_certificate_event']
cert_event_opt_group = cfg.OptGroup(name='certificate_event',
title='Certificate Event Plugin Options')
cert_event_opts = [
cfg.StrOpt('namespace',
default=DEFAULT_EVENT_PLUGIN_NAMESPACE,
help=u._('Extension namespace to search for eventing plugins.')
),
cfg.MultiStrOpt('enabled_certificate_event_plugins',
default=DEFAULT_EVENT_PLUGINS,
help=u._('List of certificate plugins to load.')
)
]
CONF.register_group(cert_event_opt_group)
CONF.register_opts(cert_event_opts, group=cert_event_opt_group)
ERROR_RETRY_MSEC = 300000
RETRY_MSEC = 3600000
CA_INFO_DEFAULT_EXPIRATION_DAYS = 1
CA_PLUGIN_TYPE_DOGTAG = "dogtag"
CA_PLUGIN_TYPE_SYMANTEC = "symantec"
# fields to distinguish CA types and subject key identifiers
CA_TYPE = "ca_type"
CA_SUBJECT_KEY_IDENTIFIER = "ca_subject_key_identifier"
# field to get the certificate request type
REQUEST_TYPE = "request_type"
# fields for the ca_id, plugin_ca_id
CA_ID = "ca_id"
PLUGIN_CA_ID = "plugin_ca_id"
# fields for ca_info dict keys
INFO_NAME = "name"
INFO_DESCRIPTION = "description"
INFO_CA_SIGNING_CERT = "ca_signing_certificate"
INFO_INTERMEDIATES = "intermediates"
INFO_EXPIRATION = "expiration"
# Singleton to avoid loading the CertificateEventManager plugins more than once
_EVENT_PLUGIN_MANAGER = None
class CertificateRequestType(object):
"""Constants to define the certificate request type."""
CUSTOM_REQUEST = "custom"
FULL_CMC_REQUEST = "full-cmc"
SIMPLE_CMC_REQUEST = "simple-cmc"
STORED_KEY_REQUEST = "stored-key"
class CertificatePluginNotFound(exception.BarbicanException):
"""Raised when no certificate plugin supporting a request is available."""
def __init__(self, plugin_name=None):
if plugin_name:
message = u._(
'Certificate plugin "{name}"'
' not found.').format(name=plugin_name)
else:
message = u._("Certificate plugin not found or configured.")
super(CertificatePluginNotFound, self).__init__(message)
class CertificatePluginNotFoundForCAID(exception.BarbicanException):
"""Raised when no certificate plugin is available for a CA_ID."""
def __init__(self, ca_id):
message = u._(
'Certificate plugin not found for "{ca_id}".').format(ca_id=ca_id)
super(CertificatePluginNotFoundForCAID, self).__init__(message)
class CertificateEventPluginNotFound(exception.BarbicanException):
"""Raised with no certificate event plugin supporting request."""
def __init__(self, plugin_name=None):
if plugin_name:
message = u._(
'Certificate event plugin "{name}" '
'not found.').format(name=plugin_name)
else:
message = u._("Certificate event plugin not found.")
super(CertificateEventPluginNotFound, self).__init__(message)
class CertificateStatusNotSupported(exception.BarbicanException):
"""Raised when cert status returned is unknown."""
def __init__(self, status):
super(CertificateStatusNotSupported, self).__init__(
u._("Certificate status of {status} not "
"supported").format(status=status)
)
self.status = status
class CertificateGeneralException(exception.BarbicanException):
"""Raised when a system fault has occurred."""
def __init__(self, reason=u._('Unknown')):
super(CertificateGeneralException, self).__init__(
u._('Problem seen during certificate processing - '
'Reason: {reason}').format(reason=reason)
)
self.reason = reason
class CertificateStatusClientDataIssue(exception.BarbicanHTTPException):
"""Raised when the CA has encountered an issue with request data."""
client_message = ""
status_code = 400
def __init__(self, reason=u._('Unknown')):
super(CertificateStatusClientDataIssue, self).__init__(
u._('Problem with data in certificate request - '
'Reason: {reason}').format(reason=reason)
)
self.client_message = self.message
class CertificateStatusInvalidOperation(exception.BarbicanHTTPException):
"""Raised when the CA has encountered an issue with request data."""
client_message = ""
status_code = 400
def __init__(self, reason=u._('Unknown')):
super(CertificateStatusInvalidOperation, self).__init__(
u._('Invalid operation requested - '
'Reason: {reason}').format(reason=reason)
)
self.client_message = self.message
class CertificateEventPluginBase(object, metaclass=abc.ABCMeta):
"""Base class for certificate eventing plugins.
This class is the base plugin contract for issuing certificate related
events from Barbican.
"""
@abc.abstractmethod
def notify_certificate_is_ready(
self, project_id, order_ref, container_ref):
"""Notify that a certificate has been generated and is ready to use.
:param project_id: Project ID associated with this certificate
:param order_ref: HATEOAS reference URI to the submitted Barbican Order
:param container_ref: HATEOAS reference URI to the Container storing
the certificate
:returns: None
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def notify_ca_is_unavailable(
self, project_id, order_ref, error_msg, retry_in_msec):
"""Notify that the certificate authority (CA) isn't available.
:param project_id: Project ID associated with this order
:param order_ref: HATEOAS reference URI to the submitted Barbican Order
:param error_msg: Error message if it is available
:param retry_in_msec: Delay before attempting to talk to the CA again.
If this is 0, then no attempt will be made.
:returns: None
"""
raise NotImplementedError # pragma: no cover
class CertificatePluginBase(object, metaclass=abc.ABCMeta):
"""Base class for certificate plugins.
This class is the base plugin contract for certificates.
"""
@abc.abstractmethod
def get_default_ca_name(self):
"""Get the default CA name
Provides a default CA name to be returned in the default
get_ca_info() method. If get_ca_info() is overridden (to
support multiple CAs for instance), then this method may not
be called. In that case, just implement this method to return
a dummy variable.
If this value is used, it should be unique amongst all the CA
plugins.
:return: The default CA name
:rtype: str
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def get_default_signing_cert(self):
"""Get the default CA signing cert
Provides a default CA signing cert to be returned in the default
get_ca_info() method. If get_ca_info() is overridden (to
support multiple CAs for instance), then this method may not
be called. In that case, just implement this method to return
a dummy variable.
:return: The default CA signing cert
:rtype: str
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def get_default_intermediates(self):
"""Get the default CA certificate chain
Provides a default CA certificate to be returned in the default
get_ca_info() method. If get_ca_info() is overridden (to
support multiple CAs for instance), then this method may not
be called. In that case, just implement this method to return
a dummy variable.
:return: The default CA certificate chain
:rtype: str
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def issue_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Create the initial order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf
:param barbican_meta_dto:
Data transfer object :class:`BarbicanMetaDTO` containing data
added to the request by the Barbican server to provide additional
context for processing, but which are not in
the original request. For example, the plugin_ca_id
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def modify_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Update the order meta-data
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf
:param barbican_meta_dto:
Data transfer object :class:`BarbicanMetaDTO` containing data
added to the request by the Barbican server to provide additional
context for processing, but which are not in
the original request. For example, the plugin_ca_id
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def cancel_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Cancel the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf
:param barbican_meta_dto:
Data transfer object :class:`BarbicanMetaDTO` containing data
added to the request by the Barbican server to provide additional
context for processing, but which are not in
the original request. For example, the plugin_ca_id
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def check_certificate_status(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Check status of the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf
:param barbican_meta_dto:
Data transfer object :class:`BarbicanMetaDTO` containing data
added to the request by the Barbican server to provide additional
context for processing, but which are not in
the original request. For example, the plugin_ca_id
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def supports(self, certificate_spec):
"""Returns if the plugin supports the certificate type.
:param certificate_spec: Contains details on the certificate to
generate the certificate order
:returns: boolean indicating if the plugin supports the certificate
type
"""
raise NotImplementedError # pragma: no cover
def supported_request_types(self):
"""Returns the request_types supported by this plugin.
:returns: a list of the Barbican-core defined request_types
supported by this plugin.
"""
return [CertificateRequestType.CUSTOM_REQUEST] # pragma: no cover
def get_ca_info(self):
"""Returns information about the CA(s) supported by this plugin.
:returns: dictionary indexed by plugin_ca_id. Each entry consists
of a dictionary of key-value pairs.
An example dictionary containing the current supported attributes
is shown below::
{ "plugin_ca_id1": {
INFO_NAME : "CA name",
INFO_DESCRIPTION : "CA user friendly description",
INFO_CA_SIGNING_CERT : "base 64 encoded signing cert",
INFO_INTERMEDIATES = "base 64 encoded certificate chain"
INFO_EXPIRATION = "ISO formatted UTC datetime for when this"
"data will become stale"
}
}
"""
name = self.get_default_ca_name()
expiration = (datetime.datetime.utcnow() +
datetime.timedelta(days=CA_INFO_DEFAULT_EXPIRATION_DAYS))
default_info = {
INFO_NAME: name,
INFO_DESCRIPTION: "Certificate Authority - {0}".format(name),
INFO_EXPIRATION: expiration.isoformat()
}
signing_cert = self.get_default_signing_cert()
if signing_cert is not None:
default_info[INFO_CA_SIGNING_CERT] = signing_cert
intermediates = self.get_default_intermediates()
if intermediates is not None:
default_info[INFO_INTERMEDIATES] = intermediates
return {name: default_info}
def supports_create_ca(self):
"""Returns whether the plugin supports on-the-fly generation of subCAs
:return: boolean, True if supported, defaults to False
"""
return False # pragma: no cover
def create_ca(self, ca_create_dto):
"""Creates a subordinate CA upon request
This call should only be made if a plugin returns True for
supports_create_ca().
:param ca_create_dto:
Data transfer object :class:`CACreateDTO` containing data
required to generate a subordinate CA. This data includes
the subject DN of the new CA signing certificate, a name for
the new CA and a reference to the CA that will issue the new
subordinate CA's signing certificate,
:return: ca_info:
Dictionary containing the data needed to create a
models.CertificateAuthority object
"""
raise NotImplementedError # pragma: no cover
def delete_ca(self, ca_id):
"""Deletes a subordinate CA
Like the create_ca call, this should only be made if the plugin
returns True for supports_create_ca()
:param ca_id: id for the CA as specified by the plugin
:return: None
"""
raise NotImplementedError # pragma: no cover
class CACreateDTO(object):
"""Class that includes data needed to create a subordinate CA """
def __init__(self, name=None, description=None, subject_dn=None,
parent_ca_id=None):
"""Creates a new CACreateDTO object.
:param name: Name for the subordinate CA
:param description: Description for the subordinate CA
:param subject_dn:
Subject DN for the new subordinate CA's signing certificate
:param parent_ca_id:
ID of the CA which is supposed to sign the subordinate CA's
signing certificate. This is ID as known to the plugin
(not the Barbican UUID)
"""
self.name = name
self.description = description
self.subject_dn = subject_dn
self.parent_ca_id = parent_ca_id
class CertificateStatus(object):
"""Defines statuses for certificate request process.
In particular:
CERTIFICATE_GENERATED - Indicates a certificate was created
WAITING_FOR_CA - Waiting for Certificate authority (CA) to complete order
CLIENT_DATA_ISSUE_SEEN - Problem was seen with client-provided data
CA_UNAVAILABLE_FOR_REQUEST - CA was not available, will try again later
REQUEST_CANCELED - The client or CA cancelled this order
INVALID_OPERATION - Unexpected error seen processing order
"""
CERTIFICATE_GENERATED = "certificate generated"
WAITING_FOR_CA = "waiting for CA"
CLIENT_DATA_ISSUE_SEEN = "client data issue seen"
CA_UNAVAILABLE_FOR_REQUEST = "CA unavailable for request"
REQUEST_CANCELED = "request canceled"
INVALID_OPERATION = "invalid operation"
class ResultDTO(object):
"""Result data transfer object (DTO).
An object of this type is returned by most certificate plugin methods, and
is used to guide follow on processing and to provide status feedback to
clients.
"""
def __init__(self, status, status_message=None, certificate=None,
intermediates=None, retry_msec=RETRY_MSEC, retry_method=None):
"""Creates a new ResultDTO.
:param status: Status for cert order
:param status_message: Message to explain status type.
:param certificate: Certificate returned from CA to be stored in
container
:param intermediates: Intermediates to be stored in container
:param retry_msec: Number of milliseconds to wait for retry
:param retry_method: Method to be called for retry, if None then retry
the current method
"""
self.status = status
self.status_message = status_message
self.certificate = certificate
self.intermediates = intermediates
self.retry_msec = int(retry_msec)
self.retry_method = retry_method
class BarbicanMetaDTO(object):
"""Barbican meta data transfer object
Information needed to process a certificate request that is not specified
in the original request, and written by Barbican core, that is needed
by the plugin to process requests.
"""
def __init__(self, plugin_ca_id=None, generated_csr=None):
"""Creates a new BarbicanMetaDTO.
:param plugin_ca_id: ca_id as known to the plugin
:param generated_csr: csr generated in the stored-key case
:return: BarbicanMetaDTO
"""
self.plugin_ca_id = plugin_ca_id
self.generated_csr = generated_csr
class CertificatePluginManager(named.NamedExtensionManager):
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
self.ca_repo = repos.get_ca_repository()
super(CertificatePluginManager, self).__init__(
conf.certificate.namespace,
conf.certificate.enabled_certificate_plugins,
invoke_on_load=False, # Defer creating plugins to utility below.
invoke_args=invoke_args,
invoke_kwds=invoke_kwargs
)
plugin_utils.instantiate_plugins(
self, invoke_args, invoke_kwargs)
def get_plugin(self, certificate_spec):
"""Gets a supporting certificate plugin.
:param certificate_spec: Contains details on the certificate to
generate the certificate order
:returns: CertificatePluginBase plugin implementation
"""
request_type = certificate_spec.get(
REQUEST_TYPE,
CertificateRequestType.CUSTOM_REQUEST)
for plugin in plugin_utils.get_active_plugins(self):
supported_request_types = plugin.supported_request_types()
if request_type not in supported_request_types:
continue
if plugin.supports(certificate_spec):
return plugin
raise CertificatePluginNotFound()
def get_plugin_by_name(self, plugin_name):
"""Gets a supporting certificate plugin.
:param plugin_name: Name of the plugin to invoke
:returns: CertificatePluginBase plugin implementation
"""
for plugin in plugin_utils.get_active_plugins(self):
if utils.generate_fullname_for(plugin) == plugin_name:
return plugin
raise CertificatePluginNotFound(plugin_name)
def get_plugin_by_ca_id(self, ca_id):
"""Gets a plugin based on the ca_id.
:param ca_id: id for CA in the CertificateAuthorities table
:returns: CertificatePluginBase plugin implementation
"""
ca = self.ca_repo.get(ca_id, suppress_exception=True)
if not ca:
raise CertificatePluginNotFoundForCAID(ca_id)
return self.get_plugin_by_name(ca.plugin_name)
def refresh_ca_table(self):
"""Refreshes the CertificateAuthority table."""
updates_made = False
for plugin in plugin_utils.get_active_plugins(self):
plugin_name = utils.generate_fullname_for(plugin)
cas, offset, limit, total = self.ca_repo.get_by_create_date(
plugin_name=plugin_name,
suppress_exception=True)
if total < 1:
# if no entries are found, then the plugin has not yet been
# queried or that plugin's entries have expired.
# Most of the time, this will be a no-op for plugins.
self.update_ca_info(plugin)
updates_made = True
if updates_made:
# commit to DB to avoid async issues with different threads
repos.commit()
def update_ca_info(self, cert_plugin):
"""Update the CA info for a particular plugin."""
plugin_name = utils.generate_fullname_for(cert_plugin)
try:
new_ca_infos = cert_plugin.get_ca_info()
except Exception as e:
# The plugin gave an invalid CA, log and return
LOG.error("ERROR getting CA from plugin: %s",
encodeutils.exception_to_unicode(e))
return
old_cas, offset, limit, total = self.ca_repo.get_by_create_date(
plugin_name=plugin_name,
suppress_exception=True,
show_expired=True)
if old_cas:
for old_ca in old_cas:
plugin_ca_id = old_ca.plugin_ca_id
if plugin_ca_id not in new_ca_infos.keys():
# remove CAs that no longer exist
self._delete_ca(old_ca)
else:
# update those that still exist
self.ca_repo.update_entity(
old_ca,
new_ca_infos[plugin_ca_id])
old_ids = set([ca.plugin_ca_id for ca in old_cas])
else:
old_ids = set()
new_ids = set(new_ca_infos.keys())
# add new CAs
add_ids = new_ids - old_ids
for add_id in add_ids:
try:
self._add_ca(plugin_name, add_id, new_ca_infos[add_id])
except Exception as e:
# The plugin gave an invalid CA, log and continue
LOG.error("ERROR adding CA from plugin: %s",
encodeutils.exception_to_unicode(e))
def _add_ca(self, plugin_name, plugin_ca_id, ca_info):
parsed_ca = dict(ca_info)
parsed_ca['plugin_name'] = plugin_name
parsed_ca['plugin_ca_id'] = plugin_ca_id
new_ca = models.CertificateAuthority(parsed_ca)
self.ca_repo.create_from(new_ca)
def _delete_ca(self, ca):
self.ca_repo.delete_entity_by_id(ca.id, None)
class _CertificateEventPluginManager(named.NamedExtensionManager,
CertificateEventPluginBase):
"""Provides services for certificate event plugins.
This plugin manager differs from others in that it implements the same
contract as the plugins that it manages. This allows eventing operations
to occur on all installed plugins (with this class acting as a composite
plugin), rather than just eventing via an individual plugin.
Each time this class is initialized it will load a new instance
of each enabled plugin. This is undesirable, so rather than initializing a
new instance of this class use the get_event_plugin_manager function
at the module level.
"""
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
super(_CertificateEventPluginManager, self).__init__(
conf.certificate_event.namespace,
conf.certificate_event.enabled_certificate_event_plugins,
invoke_on_load=False, # Defer creating plugins to utility below.
invoke_args=invoke_args,
invoke_kwds=invoke_kwargs
)
plugin_utils.instantiate_plugins(
self, invoke_args, invoke_kwargs)
def get_plugin_by_name(self, plugin_name):
"""Gets a supporting certificate event plugin.
:returns: CertificateEventPluginBase plugin implementation
"""
for plugin in plugin_utils.get_active_plugins(self):
if utils.generate_fullname_for(plugin) == plugin_name:
return plugin
raise CertificateEventPluginNotFound(plugin_name)
def notify_certificate_is_ready(
self, project_id, order_ref, container_ref):
self._invoke_certificate_plugins(
'notify_certificate_is_ready',
project_id, order_ref, container_ref)
def notify_ca_is_unavailable(
self, project_id, order_ref, error_msg, retry_in_msec):
self._invoke_certificate_plugins(
'notify_ca_is_unavailable',
project_id, order_ref, error_msg, retry_in_msec)
def _invoke_certificate_plugins(self, method, *args, **kwargs):
"""Invoke same function on plugins as calling function."""
active_plugins = plugin_utils.get_active_plugins(self)
if not active_plugins:
raise CertificateEventPluginNotFound()
for plugin in active_plugins:
getattr(plugin, method)(*args, **kwargs)
def get_event_plugin_manager():
global _EVENT_PLUGIN_MANAGER
if _EVENT_PLUGIN_MANAGER:
return _EVENT_PLUGIN_MANAGER
_EVENT_PLUGIN_MANAGER = _CertificateEventPluginManager()
return _EVENT_PLUGIN_MANAGER

View File

@ -1,160 +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.
"""
Default implementation of Barbican certificate processing plugins and support.
"""
from barbican.common import utils
from barbican.plugin.interface import certificate_manager as cert
LOG = utils.getLogger(__name__)
MSEC_UNTIL_CHECK_STATUS = 5000
class SimpleCertificatePlugin(cert.CertificatePluginBase):
"""Simple/default certificate plugin."""
def get_default_ca_name(self):
return "Simple CA"
def get_default_signing_cert(self):
return "XXXXXXXXXXXXXXXXX"
def get_default_intermediates(self):
return "YYYYYYYYYYYYYYYY"
def issue_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Create the initial order with CA
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
LOG.info('Invoking issue_certificate_request()')
return cert.ResultDTO(
cert.CertificateStatus.WAITING_FOR_CA,
retry_msec=MSEC_UNTIL_CHECK_STATUS)
def modify_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Update the order meta-data
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
LOG.info('Invoking modify_certificate_request()')
return cert.ResultDTO(cert.CertificateStatus.WAITING_FOR_CA)
def cancel_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Cancel the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
LOG.info('Invoking cancel_certificate_request()')
return cert.ResultDTO(cert.CertificateStatus.REQUEST_CANCELED)
def check_certificate_status(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Check status of the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
:returns: A :class:`ResultDTO` instance containing the result
populated by the plugin implementation
:rtype: :class:`ResultDTO`
"""
LOG.info('Invoking check_certificate_status()')
return cert.ResultDTO(cert.CertificateStatus.CERTIFICATE_GENERATED)
def supports(self, certificate_spec):
"""Indicates whether the plugin supports the certificate type.
:param certificate_spec: Contains details on the certificate to
generate the certificate order
:returns: boolean indicating if the plugin supports the certificate
type
"""
return True
def supported_request_types(self):
"""Returns the request types supported by this plugin.
:returns: dict containing Barbican-core defined request types
supported by this plugin.
"""
return [cert.CertificateRequestType.CUSTOM_REQUEST,
cert.CertificateRequestType.SIMPLE_CMC_REQUEST,
cert.CertificateRequestType.FULL_CMC_REQUEST,
cert.CertificateRequestType.STORED_KEY_REQUEST]
class SimpleCertificateEventPlugin(cert.CertificateEventPluginBase):
"""Simple/default certificate event plugin."""
def notify_certificate_is_ready(
self, project_id, order_ref, container_ref):
"""Notify that a certificate has been generated and is ready to use.
:param project_id: Project ID associated with this certificate
:param order_ref: HATEOAS reference URI to the submitted Barbican Order
:param container_ref: HATEOAS reference URI to the Container storing
the certificate
:returns: None
"""
LOG.info('Invoking notify_certificate_is_ready()')
def notify_ca_is_unavailable(
self, project_id, order_ref, error_msg, retry_in_msec):
"""Notify that the certificate authority (CA) isn't available.
:param project_id: Project ID associated with this order
:param order_ref: HATEOAS reference URI to the submitted Barbican Order
:param error_msg: Error message if it is available
:param retry_in_msec: Delay before attempting to talk to the CA again.
If this is 0, then no attempt will be made.
:returns: None
"""
LOG.info('Invoking notify_ca_is_unavailable()')

View File

@ -1,479 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# 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.
import base64
import datetime
import fnmatch
import os
import re
import subprocess # nosec
from tempfile import mkstemp
import uuid
from OpenSSL import crypto
from oslo_config import cfg
from oslo_utils import uuidutils
from barbican.common import config
from barbican.common import utils
from barbican import i18n as u
import barbican.plugin.interface.certificate_manager as cert_manager
CONF = config.new_config()
LOG = utils.getLogger(__name__)
snakeoil_ca_plugin_group = cfg.OptGroup(name='snakeoil_ca_plugin',
title="Snakeoil CA Plugin Options")
snakeoil_ca_plugin_opts = [
cfg.StrOpt('ca_cert_path',
help=u._('Path to CA certificate file')),
cfg.StrOpt('ca_cert_key_path',
help=u._('Path to CA certificate key file')),
cfg.StrOpt('ca_cert_chain_path',
help=u._('Path to CA certificate chain file')),
cfg.StrOpt('ca_cert_pkcs7_path',
help=u._('Path to CA chain pkcs7 file')),
cfg.StrOpt('subca_cert_key_directory',
default='/etc/barbican/snakeoil-cas',
help=u._('Directory in which to store certs/keys for subcas')),
]
CONF.register_group(snakeoil_ca_plugin_group)
CONF.register_opts(snakeoil_ca_plugin_opts, group=snakeoil_ca_plugin_group)
config.parse_args(CONF)
def list_opts():
yield snakeoil_ca_plugin_group, snakeoil_ca_plugin_opts
def set_subject_X509Name(target, dn):
"""Set target X509Name object with parsed dn.
This is very basic and should certainly be replaced by something using
cryptography for instance, but will do for a basic test CA
"""
# TODO(alee) Figure out why C (country) is not working
fields = dn.split(',')
for field in fields:
m = re.search(r"(\w+)\s*=\s*(.+)", field.strip())
name = m.group(1)
value = m.group(2)
if name.lower() == 'ou':
target.OU = value
elif name.lower() == 'st':
target.ST = value
elif name.lower() == 'cn':
target.CN = value
elif name.lower() == 'l':
target.L = value
elif name.lower() == 'o':
target.O = value # noqa: E741
return target
class SnakeoilCA(object):
def __init__(self, cert_path=None, key_path=None, chain_path=None,
pkcs7_path=None, name=None, serial=1,
key_size=2048, expiry_days=10 * 365, x509_version=2,
subject_dn=None, signing_dn=None, signing_key=None,
parent_chain_path=None):
self.cert_path = cert_path
self.key_path = key_path
self.chain_path = chain_path
self.pkcs7_path = pkcs7_path
self.name = name
self.serial = serial
self.key_size = key_size
self.expiry_days = expiry_days
self.x509_version = x509_version
self.subject_dn = subject_dn
if signing_dn is not None:
self.signing_dn = signing_dn
else:
self.signing_dn = subject_dn # self-signed
self.signing_key = signing_key
self.parent_chain_path = parent_chain_path
self._cert_val = None
self._key_val = None
self._chain_val = None
self._pkcs7_val = None
@property
def cert(self):
self.ensure_exists()
if self.cert_path:
with open(self.cert_path, 'rb') as cert_fh:
return crypto.load_certificate(crypto.FILETYPE_PEM,
cert_fh.read())
else:
return crypto.load_certificate(crypto.FILETYPE_PEM, self._cert_val)
@cert.setter
def cert(self, val):
if self.cert_path:
with open(self.cert_path, 'wb') as cert_fh:
cert_fh.write(crypto.dump_certificate(crypto.FILETYPE_PEM,
val))
else:
self._cert_val = crypto.dump_certificate(crypto.FILETYPE_PEM, val)
@property
def key(self):
self.ensure_exists()
if self.key_path:
with open(self.key_path, 'rb') as key_fh:
return crypto.load_privatekey(crypto.FILETYPE_PEM,
key_fh.read())
else:
return crypto.load_privatekey(crypto.FILETYPE_PEM, self._key_val)
@key.setter
def key(self, val):
if self.key_path:
with open(self.key_path, 'wb') as key_fh:
key_fh.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, val))
else:
self._key_val = crypto.dump_privatekey(crypto.FILETYPE_PEM, val)
@property
def chain(self):
self.ensure_exists()
if self.chain_path:
with open(self.chain_path, 'rb') as chain_fh:
return chain_fh.read()
else:
return self._chain_val
@chain.setter
def chain(self, val):
if self.chain_path:
with open(self.chain_path, 'wb') as chain_fh:
chain_fh.write(val)
else:
self._chain_val = val
@property
def pkcs7(self):
self.ensure_exists()
if self.pkcs7_path:
with open(self.pkcs7_path, 'rb') as pkcs7_fh:
return pkcs7_fh.read()
else:
return self._pkcs7_val
@pkcs7.setter
def pkcs7(self, val):
if self.pkcs7_path:
with open(self.pkcs7_path, 'wb') as pkcs7_fh:
pkcs7_fh.write(val)
else:
self._pkcs7_val = val
@property
def exists(self):
if self.cert_path is not None:
cert_exists = os.path.isfile(self.cert_path)
else:
cert_exists = self._cert_val is not None
if self.key_path is not None:
key_exists = os.path.isfile(self.key_path)
else:
key_exists = self._key_val is not None
if self.chain_path is not None:
chain_exists = os.path.isfile(self.chain_path)
else:
chain_exists = self._chain_val is not None
if self.pkcs7_path is not None:
pkcs7_exists = os.path.isfile(self.pkcs7_path)
else:
pkcs7_exists = self._pkcs7_val is not None
return (cert_exists and key_exists and
pkcs7_exists and chain_exists)
def ensure_exists(self):
if not self.exists:
LOG.debug('Keypair not found, creating new cert/key')
self.cert, self.key, self.chain, self.pkcs7 = (
self.create_keypair())
def create_keypair(self):
LOG.debug('Generating Snakeoil CA')
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, self.key_size)
cert = crypto.X509()
cert.set_version(self.x509_version)
cert.set_serial_number(self.serial)
subject = cert.get_subject()
set_subject_X509Name(subject, self.subject_dn)
cert.set_subject(subject)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(self.expiry_days)
cert.set_issuer(set_subject_X509Name(
cert.get_issuer(), self.signing_dn))
cert.set_pubkey(key)
cert.add_extensions([
crypto.X509Extension(b"basicConstraints", True,
b"CA:TRUE, pathlen:5"),
])
if not self.signing_key:
self.signing_key = key # self-signed
cert.sign(self.signing_key, 'sha256')
LOG.debug('Snakeoil CA cert/key generated')
chain = b''
if self.parent_chain_path:
with open(self.parent_chain_path, 'rb') as fh:
chain = fh.read()
chain += crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
pkcs7 = self._generate_pkcs7(chain)
return cert, key, chain, pkcs7
def _generate_pkcs7(self, chain):
fin, temp_in = mkstemp()
os.write(fin, chain)
os.close(fin)
fout, temp_out = mkstemp()
os.close(fout)
subprocess.call(['/usr/bin/openssl', 'crl2pkcs7', '-nocrl', # nosec
'-out', temp_out, '-certfile', temp_in], shell=False)
with open(temp_out, 'rb') as pkcs7_fh:
pkcs7 = pkcs7_fh.read()
os.remove(temp_in)
os.remove(temp_out)
return pkcs7
class CertManager(object):
def __init__(self, ca):
self.ca = ca
def get_new_serial(self):
return uuid.uuid4().int
def make_certificate(self, csr, expires=2 * 365):
cert = crypto.X509()
cert.set_serial_number(self.get_new_serial())
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(expires)
cert.set_issuer(self.ca.cert.get_subject())
cert.set_subject(csr.get_subject())
cert.set_pubkey(csr.get_pubkey())
cert.sign(self.ca.key, 'sha256')
return cert
class SnakeoilCACertificatePlugin(cert_manager.CertificatePluginBase):
"""Snakeoil CA certificate plugin.
This is used for easily generating certificates which are not useful in a
production environment.
"""
def __init__(self, conf=CONF):
self.cas = {}
self.ca = SnakeoilCA(
cert_path=conf.snakeoil_ca_plugin.ca_cert_path,
key_path=conf.snakeoil_ca_plugin.ca_cert_key_path,
chain_path=conf.snakeoil_ca_plugin.ca_cert_chain_path,
pkcs7_path=conf.snakeoil_ca_plugin.ca_cert_pkcs7_path,
name=self.get_default_ca_name(),
subject_dn="cn=Snakeoil Certificate,o=example.com"
)
self.cas[self.get_default_ca_name()] = self.ca
self.subca_directory = conf.snakeoil_ca_plugin.subca_cert_key_directory
if self.subca_directory:
if not os.path.exists(self.subca_directory):
os.makedirs(self.subca_directory) # pragma: no cover
else:
self._reload_previously_created_subcas()
self.cert_manager = CertManager(self.ca)
def _reload_previously_created_subcas(self):
for file in os.listdir(self.subca_directory):
if fnmatch.fnmatch(file, '*.key'):
ca_id, _ext = os.path.splitext(file)
self.cas[ca_id] = SnakeoilCA(
cert_path=os.path.join(self.subca_directory,
ca_id + ".cert"),
key_path=os.path.join(self.subca_directory, file),
chain_path=os.path.join(self.subca_directory,
ca_id + ".chain"),
pkcs7_path=os.path.join(self.subca_directory,
ca_id + ".p7b")
)
def get_default_ca_name(self):
return "Snakeoil CA"
def get_default_signing_cert(self):
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.ca.cert)
def get_default_intermediates(self):
return None
def supported_request_types(self):
return [cert_manager.CertificateRequestType.CUSTOM_REQUEST,
cert_manager.CertificateRequestType.STORED_KEY_REQUEST]
def issue_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
if barbican_meta_dto.generated_csr is not None:
encoded_csr = barbican_meta_dto.generated_csr
else:
try:
encoded_csr = base64.b64decode(order_meta['request_data'])
except KeyError:
return cert_manager.ResultDTO(
cert_manager.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
status_message=u._("No request_data specified"))
csr = crypto.load_certificate_request(crypto.FILETYPE_PEM, encoded_csr)
ca_id = barbican_meta_dto.plugin_ca_id
if ca_id:
ca = self.cas.get(ca_id)
if ca is None:
raise cert_manager.CertificateGeneralException(
"Invalid ca_id passed into snake oil plugin:" + ca_id)
else:
ca = self.ca
cert_mgr = CertManager(ca)
cert = cert_mgr.make_certificate(csr)
cert_enc = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
return cert_manager.ResultDTO(
cert_manager.CertificateStatus.CERTIFICATE_GENERATED,
certificate=base64.b64encode(cert_enc),
intermediates=base64.b64encode(ca.pkcs7))
def modify_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
raise NotImplementedError
def cancel_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
raise NotImplementedError
def check_certificate_status(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
raise NotImplementedError
def supports(self, certificate_spec):
request_type = certificate_spec.get(
cert_manager.REQUEST_TYPE,
cert_manager.CertificateRequestType.CUSTOM_REQUEST)
return request_type in self.supported_request_types()
def supports_create_ca(self):
return True
def create_ca(self, ca_create_dto):
# get the parent CA from the ca list, return error if not on list
parent_ca_id = ca_create_dto.parent_ca_id
if not parent_ca_id:
raise cert_manager.CertificateGeneralException(
"No parent id passed to snake oil plugin on create_ca")
parent_ca = self.cas.get(parent_ca_id)
if not parent_ca:
raise cert_manager.CertificateGeneralException(
"Invalid parent id passed to snake oil plugin:" + parent_ca_id)
# create a new ca, passing in key and issuer from the parent
new_ca_id = uuidutils.generate_uuid()
new_cert_path = os.path.join(self.subca_directory, new_ca_id + ".cert")
new_key_path = os.path.join(self.subca_directory, new_ca_id + ".key")
new_chain_path = os.path.join(self.subca_directory,
new_ca_id + ".chain")
new_pkcs7_path = os.path.join(self.subca_directory,
new_ca_id + ".p7b")
parent_chain_path = parent_ca.chain_path
new_ca = SnakeoilCA(cert_path=new_cert_path,
key_path=new_key_path,
chain_path=new_chain_path,
pkcs7_path=new_pkcs7_path,
name=ca_create_dto.name,
subject_dn=ca_create_dto.subject_dn,
signing_dn=parent_ca.subject_dn,
signing_key=parent_ca.key,
parent_chain_path=parent_chain_path)
self.cas[new_ca_id] = new_ca
expiration = (datetime.datetime.utcnow() + datetime.timedelta(
days=cert_manager.CA_INFO_DEFAULT_EXPIRATION_DAYS))
return {
cert_manager.INFO_NAME: new_ca.name,
cert_manager.INFO_CA_SIGNING_CERT: crypto.dump_certificate(
crypto.FILETYPE_PEM, new_ca.cert),
cert_manager.INFO_EXPIRATION: expiration.isoformat(),
cert_manager.INFO_INTERMEDIATES: new_ca.pkcs7,
cert_manager.PLUGIN_CA_ID: new_ca_id
}
def get_ca_info(self):
expiration = (datetime.datetime.utcnow() + datetime.timedelta(
days=cert_manager.CA_INFO_DEFAULT_EXPIRATION_DAYS))
ret = {}
for ca_id, ca in self.cas.items():
ca_info = {
cert_manager.INFO_NAME: ca.name,
cert_manager.INFO_CA_SIGNING_CERT: crypto.dump_certificate(
crypto.FILETYPE_PEM, ca.cert),
cert_manager.INFO_INTERMEDIATES: ca.pkcs7,
cert_manager.INFO_EXPIRATION: expiration.isoformat()
}
ret[ca_id] = ca_info
return ret
def delete_ca(self, ca_id):
self.cas.pop(ca_id)
ca_files = [os.path.join(self.subca_directory, ca_id + ".cert"),
os.path.join(self.subca_directory, ca_id + ".key"),
os.path.join(self.subca_directory, ca_id + ".chain"),
os.path.join(self.subca_directory, ca_id + ".p7b")]
for ca_file in ca_files:
if os.path.exists(ca_file):
os.remove(ca_file)

View File

@ -1,306 +0,0 @@
# Copyright (c) 2013-2014 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.
"""
Barbican certificate processing plugins and support.
"""
from oslo_config import cfg
from requests import exceptions as request_exceptions
from symantecssl.core import Symantec
from symantecssl import exceptions as symantec_exceptions
from barbican.common import config
from barbican.common import utils
from barbican import i18n as u
from barbican.plugin.interface import certificate_manager as cert
CONF = config.new_config()
LOG = utils.getLogger(__name__)
symantec_plugin_group = cfg.OptGroup(name='symantec_plugin',
title='Symantec Plugin Options')
symantec_plugin_opts = [
cfg.StrOpt('username',
deprecated_for_removal=True,
deprecated_reason=('Symantec certificate plugin has been '
'deprecated'),
help=u._('Symantec username for authentication')),
cfg.StrOpt('password',
deprecated_for_removal=True,
deprecated_reason=('Symantec certificate plugin has been '
'deprecated'),
help=u._('Symantec password for authentication'),
secret=True),
cfg.StrOpt('url',
deprecated_for_removal=True,
deprecated_reason=('Symantec certificate plugin has been '
'deprecated'),
help=u._('Domain of Symantec API'))
]
CONF.register_group(symantec_plugin_group)
CONF.register_opts(symantec_plugin_opts, group=symantec_plugin_group)
config.parse_args(CONF)
class SymantecCertificatePlugin(cert.CertificatePluginBase):
"""Symantec certificate plugin."""
def __init__(self, conf=CONF):
self.username = conf.symantec_plugin.username
self.password = conf.symantec_plugin.password
self.url = conf.symantec_plugin.url
if self.username is None:
raise ValueError(u._("username is required"))
if self.password is None:
raise ValueError(u._("password is required"))
if self.url is None:
raise ValueError(u._("url is required"))
LOG.warning('Symantec certificate plugin has been deprecated and '
'will be removed in a future release.')
def get_default_ca_name(self):
return "Symantec CA"
def get_default_signing_cert(self):
# TODO(chellygel) Add code to get the signing cert
return None
def get_default_intermediates(self):
# TODO(chellygel) Add code to get the cert chain
return None
def issue_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Create the initial order with CA
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
:returns: ResultDTO
"""
successful, error_msg, can_retry = _ca_create_order(order_meta,
plugin_meta)
status = cert.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST
message = None
if successful:
status = cert.CertificateStatus.WAITING_FOR_CA
elif can_retry:
status = cert.CertificateStatus.CLIENT_DATA_ISSUE_SEEN
message = error_msg
return cert.ResultDTO(status=status, status_message=message)
def modify_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Update the order meta-data
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
"""
raise NotImplementedError # pragma: no cover
def cancel_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Cancel the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
"""
raise NotImplementedError # pragma: no cover
def check_certificate_status(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Check status of the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
"""
raise NotImplementedError # pragma: no cover
def supports(self, certificate_spec):
"""Indicates if the plugin supports the certificate type.
:param certificate_spec: Contains details on the certificate to
generate the certificate order
:returns: boolean indicating if the plugin supports the certificate
type
"""
# TODO(chellygel): Research what certificate types are supported by
# symantec. Returning True for testing purposes
return True
def _ca_create_order(self, order_meta, plugin_meta):
"""Creates an order with the Symantec CA.
The PartnerOrderId and GeoTrustOrderId are returned and stored in
plugin_meta. PartnerCode and ProductCode are also stored in plugin_meta
for future use.
All required order parameters must be stored as a dict in
order_meta.
Required fields are:
PartnerCode, ProductCode, PartnerOrderId, OrganizationName,
AddressLine1, City, Region, PostalCode, Country, OrganizationPhone
ValidityPeriod, ServerCount, WebServerType, AdminContactFirstName,
AdminContactLastName, AdminContactPhone, AdminContactEmail,
AdminContactTitle, AdminContactAddressLine1, AdminContactCity,
AdminContactRegion, AdminContactPostalCode, AdminContactCountry,
BillingContact*, TechContact*, and CSR.
*The Billing and Tech contact information follows the same convention
as the AdminContact fields.
Optional Parameters: TechSameAsAdmin, BillSameAsAdmin, more options can be
found in Symantec's API docs. Contact Symantec for the API document.
:returns: tuple with success, error message, and can retry
"""
api = Symantec(self.username, self.password, self.url)
try:
order_data = api.order(**order_meta)
# GeotrustOrderId is used to handle emails from Symantec.
# PartnerCode and ProductCode are being stored in plugin_meta for
# convenience when calling _ca_get_order_status, _ca_modify_order, etc.
plugin_meta["GeotrustOrderID"] = order_data["GeotrustOrderID"]
plugin_meta["PartnerOrderID"] = order_data["PartnerOrderID"]
plugin_meta["PartnerCode"] = order_meta["OrderDetails"]["PartnerCode"]
plugin_meta["ProductCode"] = order_meta["OrderDetails"]["ProductCode"]
return True, None, False
except symantec_exceptions.SymantecError as e:
return False, e, False
except request_exceptions.RequestException as e:
return False, e, True
def _ca_get_order_status(self, plugin_meta):
"""Sends a request to the Symantec CA for details on an order.
Parameters needed for GetOrderByPartnerOrderID:
plugin_meta parameters: PartnerOrderId, PartnerCode
If the order is complete, the Certificate is returned as a string.
returns: tuple with success, error message, can retry,
and the certificate (if available).
"""
api = Symantec(self.username, self.password, self.url)
order_details = {
"PartnerOrderID": plugin_meta["PartnerOrderID"],
"PartnerCode": plugin_meta["PartnerCode"],
"ReturnCertificateInfo": "TRUE",
"ReturnFulfillment": "TRUE",
"ReturnCaCerts": "TRUE",
}
try:
order_data = api.get_order_by_partner_order_id(**order_details)
if order_data["OrderInfo"]["OrderState"] == "COMPLETED":
ca = order_data["Fulfillment"]["CACertificates"]["CACertificate"]
return True, None, False, ca["CACert"]
return True, None, False, None
except symantec_exceptions.SymantecError as e:
return False, e, False, None
except request_exceptions.RequestException as e:
return False, e, True, None
def _ca_modify_order(self, order_meta, plugin_meta):
"""Sends a request to the Symantec CA to modify an order.
Parameters needed for modifyOrder:
PartnerOrderID - Needed to specify order
PartnerCode - Needed to specify order
ProductCode - Needed to specify order
Also need a dict, order_meta with the parameters/values to modify.
returns: tuple with success, error message, and can retry.
"""
api = Symantec(self.username, self.password, self.url)
order_details = {
"PartnerOrderID": plugin_meta["PartnerOrderID"],
"PartnerCode": plugin_meta["PartnerCode"],
"ProductCode": plugin_meta["ProductCode"],
}
order_details.update(order_meta)
try:
api.validate_order_parameters(**order_details)
return True, None, False
except symantec_exceptions.SymantecError as e:
return False, e, False
except request_exceptions.RequestException as e:
return False, e, True
def _ca_cancel_order(self, plugin_meta):
"""Sends a request to the Symantec CA to cancel an order.
Parameters needed for modifyOrder:
PartnerOrderID - Needed to specify order
PartnerCode - Needed to specify order
ProductCode - Needed to specify order
returns: tuple with success, error message, and can retry.
"""
api = Symantec(self.username, self.password, self.url)
order_details = {
"PartnerOrderID": plugin_meta["PartnerOrderID"],
"PartnerCode": plugin_meta["PartnerCode"],
"ProductCode": plugin_meta["ProductCode"],
"ModifyOrderOperation": "CANCEL",
}
try:
api.modify_order(**order_details)
return True, None, False
except symantec_exceptions.SymantecError as e:
return False, e, False
except request_exceptions.RequestException as e:
return False, e, True

View File

@ -46,13 +46,6 @@ class TaskClient(object):
project_id=project_id, project_id=project_id,
request_id=request_id) request_id=request_id)
def check_certificate_status(self, order_id, project_id, request_id):
"""Check the status of a certificate order."""
self._cast('check_certificate_status',
order_id=order_id,
project_id=project_id,
request_id=request_id)
def _cast(self, name, **kwargs): def _cast(self, name, **kwargs):
"""Asynchronous call handler. Barbican probably only needs casts. """Asynchronous call handler. Barbican probably only needs casts.

View File

@ -43,9 +43,7 @@ LOG = utils.getLogger(__name__)
# Maps the common/shared RetryTasks (returned from lower-level business logic # Maps the common/shared RetryTasks (returned from lower-level business logic
# and plugin processing) to top-level RPC tasks in the Tasks class below. # and plugin processing) to top-level RPC tasks in the Tasks class below.
MAP_RETRY_TASKS = { MAP_RETRY_TASKS = {}
common.RetryTasks.INVOKE_CERT_STATUS_CHECK_TASK: 'check_certificate_status'
}
def find_function_name(func, if_no_name=None): def find_function_name(func, if_no_name=None):
@ -211,20 +209,6 @@ class Tasks(object):
return resources.BeginTypeOrder().process_and_suppress_exceptions( return resources.BeginTypeOrder().process_and_suppress_exceptions(
order_id, project_id) order_id, project_id)
@monitored
@transactional
@retryable_order
def check_certificate_status(self, context, order_id,
project_id, request_id):
"""Check the status of a certificate order."""
message = "Processing check certificate status on order: " \
"order ID is '%(order)s' and request ID is '%(request)s'"
LOG.info(message, {'order': order_id, 'request': request_id})
check_cert_order = resources.CheckCertificateStatusOrder()
return check_cert_order.process_and_suppress_exceptions(
order_id, project_id)
class TaskServer(Tasks, service.Service): class TaskServer(Tasks, service.Service):
"""Server to process asynchronous tasking from Barbican API nodes. """Server to process asynchronous tasking from Barbican API nodes.

View File

@ -1,594 +0,0 @@
# Copyright (c) 2013-2014 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.
from ldap3.utils.dn import parse_dn
from OpenSSL import crypto
from barbican.common import exception as excep
from barbican.common import hrefs
from barbican.common import resources as res
import barbican.common.utils as utils
from barbican.model import models
from barbican.model import repositories as repos
from barbican.plugin.interface import certificate_manager as cert
from barbican.plugin import resources as plugin
from barbican.tasks import common
LOG = utils.getLogger(__name__)
# Order sub-status definitions
ORDER_STATUS_REQUEST_PENDING = models.OrderStatus(
"cert_request_pending",
"Request has been submitted to the CA. "
"Waiting for certificate to be generated"
)
ORDER_STATUS_CERT_GENERATED = models.OrderStatus(
"cert_generated",
"Certificate has been generated"
)
ORDER_STATUS_DATA_INVALID = models.OrderStatus(
"cert_data_invalid",
"CA rejected request data as invalid"
)
ORDER_STATUS_CA_UNAVAIL_FOR_ISSUE = models.OrderStatus(
"cert_ca_unavail_for_issue",
"Unable to submit certificate request. CA unavailable"
)
ORDER_STATUS_INVALID_OPERATION = models.OrderStatus(
"cert_invalid_operation",
"CA returned invalid operation"
)
ORDER_STATUS_INTERNAL_ERROR = models.OrderStatus(
"cert_internal_error",
"Internal error during certificate operations"
)
ORDER_STATUS_CA_UNAVAIL_FOR_CHECK = models.OrderStatus(
"cert_ca_unavail_for_status_check",
"Unable to get certificate request status. CA unavailable."
)
def refresh_certificate_resources():
# Before CA operations can be performed, the CA table must be populated
cert.CertificatePluginManager().refresh_ca_table()
def issue_certificate_request(order_model, project_model, result_follow_on):
"""Create the initial order with CA.
Note that this method may be called more than once if retries are
required. Barbican metadata is used to store intermediate information,
including selected plugins by name, to support such retries.
:param: order_model - order associated with this cert request
:param: project_model - project associated with this request
:param: result_follow_on - A :class:`FollowOnProcessingStatusDTO` instance
instantiated by the client that this function may optionally update
with information on how to process this task into the future.
:returns: container_model - container with the relevant cert if
the request has been completed. None otherwise
"""
plugin_meta = _get_plugin_meta(order_model)
barbican_meta = _get_barbican_meta(order_model)
# TODO(john-wood-w) We need to de-conflict barbican_meta (stored with order
# and not shown to plugins) with barbican_meta_dto (shared with plugins).
# As a minimum we should change the name of the DTO to something like
# 'extended_meta_dto' or some such.
barbican_meta_for_plugins_dto = cert.BarbicanMetaDTO()
# refresh the CA table. This is mostly a no-op unless the entries
# for a plugin are expired.
cert.CertificatePluginManager().refresh_ca_table()
cert_plugin = _get_cert_plugin(barbican_meta,
barbican_meta_for_plugins_dto,
order_model, project_model)
barbican_meta['plugin_name'] = utils.generate_fullname_for(cert_plugin)
# Generate CSR if needed.
request_type = order_model.meta.get(cert.REQUEST_TYPE)
if request_type == cert.CertificateRequestType.STORED_KEY_REQUEST:
csr = barbican_meta.get('generated_csr')
if csr is None:
# TODO(alee) Fix this to be a non-project specific call once
# the ACL patches go in.
csr = _generate_csr_from_private_key(order_model, project_model)
barbican_meta['generated_csr'] = csr
barbican_meta_for_plugins_dto.generated_csr = csr
result = cert_plugin.issue_certificate_request(
order_model.id, order_model.meta,
plugin_meta, barbican_meta_for_plugins_dto)
# Save plugin and barbican metadata for this order.
_save_plugin_metadata(order_model, plugin_meta)
_save_barbican_metadata(order_model, barbican_meta)
# Handle result
return _handle_task_result(
result, result_follow_on, order_model, project_model, request_type,
unavailable_status=ORDER_STATUS_CA_UNAVAIL_FOR_ISSUE)
def _get_cert_plugin(barbican_meta, barbican_meta_for_plugins_dto,
order_model, project_model):
cert_plugin_name = barbican_meta.get('plugin_name')
if cert_plugin_name:
return cert.CertificatePluginManager().get_plugin_by_name(
cert_plugin_name)
ca_id = _get_ca_id(order_model.meta, project_model.id)
if ca_id:
ca = repos.get_ca_repository().get(ca_id)
barbican_meta_for_plugins_dto.plugin_ca_id = ca.plugin_ca_id
return cert.CertificatePluginManager().get_plugin_by_name(
ca.plugin_name)
else:
return cert.CertificatePluginManager().get_plugin(order_model.meta)
def check_certificate_request(order_model, project_model, result_follow_on):
"""Check the status of a certificate request with the CA.
Note that this method may be called more than once if retries are
required. Barbican metadata is used to store intermediate information,
including selected plugins by name, to support such retries.
:param: order_model - order associated with this cert request
:param: project_model - project associated with this request
:param: result_follow_on - A :class:`FollowOnProcessingStatusDTO` instance
instantiated by the client that this function may optionally update
with information on how to process this task into the future.
:returns: container_model - container with the relevant cert if the
request has been completed. None otherwise.
"""
plugin_meta = _get_plugin_meta(order_model)
barbican_meta = _get_barbican_meta(order_model)
# TODO(john-wood-w) See note above about DTO's name.
barbican_meta_for_plugins_dto = cert.BarbicanMetaDTO()
cert_plugin = cert.CertificatePluginManager().get_plugin_by_name(
barbican_meta.get('plugin_name'))
result = cert_plugin.check_certificate_status(
order_model.id, order_model.meta,
plugin_meta, barbican_meta_for_plugins_dto)
# Save plugin order plugin state
_save_plugin_metadata(order_model, plugin_meta)
request_type = order_model.meta.get(cert.REQUEST_TYPE)
return _handle_task_result(
result, result_follow_on, order_model, project_model, request_type,
unavailable_status=ORDER_STATUS_CA_UNAVAIL_FOR_CHECK)
def create_subordinate_ca(project_model, name, description, subject_dn,
parent_ca_ref, creator_id):
"""Create a subordinate CA
:param name - name of the subordinate CA
:param: description - description of the subordinate CA
:param: subject_dn - subject DN of the subordinate CA
:param: parent_ca_ref - Barbican URL reference to the parent CA
:param: creator_id - id for creator of the subordinate CA
:return: :class models.CertificateAuthority model object for new sub CA
"""
# check that the parent ref exists and is accessible
parent_ca_id = hrefs.get_ca_id_from_ref(parent_ca_ref)
ca_repo = repos.get_ca_repository()
parent_ca = ca_repo.get(entity_id=parent_ca_id, suppress_exception=True)
if not parent_ca:
raise excep.InvalidParentCA(parent_ca_ref=parent_ca_ref)
# Parent CA must be a base CA or a subCA owned by this project
if (parent_ca.project_id is not None and
parent_ca.project_id != project_model.id):
raise excep.UnauthorizedSubCA()
# get the parent plugin, raises CertPluginNotFound if missing
cert_plugin = cert.CertificatePluginManager().get_plugin_by_name(
parent_ca.plugin_name)
# confirm that the plugin supports creating subordinate CAs
if not cert_plugin.supports_create_ca():
raise excep.SubCAsNotSupported()
# make call to create the subordinate ca
create_ca_dto = cert.CACreateDTO(
name=name,
description=description,
subject_dn=subject_dn,
parent_ca_id=parent_ca.plugin_ca_id)
new_ca_dict = cert_plugin.create_ca(create_ca_dto)
if not new_ca_dict:
raise excep.SubCANotCreated(name=name)
# create and store the subordinate CA as a new certificate authority object
new_ca_dict['plugin_name'] = parent_ca.plugin_name
new_ca_dict['creator_id'] = creator_id
new_ca_dict['project_id'] = project_model.id
new_ca = models.CertificateAuthority(new_ca_dict)
ca_repo.create_from(new_ca)
return new_ca
def delete_subordinate_ca(external_project_id, ca):
"""Deletes a subordinate CA and any related artifacts
:param external_project_id: external project ID
:param ca: class:`models.CertificateAuthority` to be deleted
:return: None
"""
# TODO(alee) See if the checks below can be moved to the RBAC code
# Check that this CA is a subCA
if ca.project_id is None:
raise excep.CannotDeleteBaseCA()
# Check that the user's project owns this subCA
project = res.get_or_create_project(external_project_id)
if ca.project_id != project.id:
raise excep.UnauthorizedSubCA()
project_ca_repo = repos.get_project_ca_repository()
(project_cas, _, _, _) = project_ca_repo.get_by_create_date(
project_id=project.id, ca_id=ca.id,
suppress_exception=True)
preferred_ca_repo = repos.get_preferred_ca_repository()
(preferred_cas, _, _, _) = preferred_ca_repo.get_by_create_date(
project_id=project.id, ca_id=ca.id, suppress_exception=True)
# Can not delete a project preferred CA, if other project CAs exist. One
# of those needs to be designated as the preferred CA first.
if project_cas and preferred_cas and not is_last_project_ca(project.id):
raise excep.CannotDeletePreferredCA()
# Remove the CA as preferred
if preferred_cas:
preferred_ca_repo.delete_entity_by_id(preferred_cas[0].id,
external_project_id)
# Remove the CA from project list
if project_cas:
project_ca_repo.delete_entity_by_id(project_cas[0].id,
external_project_id)
# Delete the CA entry from plugin
cert_plugin = cert.CertificatePluginManager().get_plugin_by_name(
ca.plugin_name)
cert_plugin.delete_ca(ca.plugin_ca_id)
# Finally, delete the CA entity from the CA repository
ca_repo = repos.get_ca_repository()
ca_repo.delete_entity_by_id(
entity_id=ca.id,
external_project_id=external_project_id)
def is_last_project_ca(project_id):
"""Returns True iff project has exactly one project CA
:param project_id: internal project ID
:return: Boolean
"""
project_ca_repo = repos.get_project_ca_repository()
_, _, _, total = project_ca_repo.get_by_create_date(
project_id=project_id,
suppress_exception=True
)
return total == 1
def _handle_task_result(result, result_follow_on, order_model,
project_model, request_type, unavailable_status):
if cert.CertificateStatus.WAITING_FOR_CA == result.status:
_update_result_follow_on(
result_follow_on,
order_status=ORDER_STATUS_REQUEST_PENDING,
retry_task=common.RetryTasks.INVOKE_CERT_STATUS_CHECK_TASK,
retry_msec=result.retry_msec)
elif cert.CertificateStatus.CERTIFICATE_GENERATED == result.status:
_update_result_follow_on(
result_follow_on,
order_status=ORDER_STATUS_CERT_GENERATED)
container_model = _save_secrets(result, project_model, request_type,
order_model)
return container_model
elif cert.CertificateStatus.CLIENT_DATA_ISSUE_SEEN == result.status:
raise cert.CertificateStatusClientDataIssue(result.status_message)
elif cert.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST == result.status:
_update_result_follow_on(
result_follow_on,
order_status=unavailable_status,
retry_task=common.RetryTasks.INVOKE_SAME_TASK,
retry_msec=cert.ERROR_RETRY_MSEC)
_notify_ca_unavailable(order_model, result)
elif cert.CertificateStatus.INVALID_OPERATION == result.status:
raise cert.CertificateStatusInvalidOperation(result.status_message)
else:
raise cert.CertificateStatusNotSupported(result.status)
return None
def _add_private_key_to_generated_cert_container(container_id, order_model,
project_model):
keypair_container_id, keypair_container = _get_container_from_order_meta(
order_model, project_model)
private_key_id = None
for cs in keypair_container.container_secrets:
if cs.name == 'private_key':
private_key_id = cs.secret_id
new_consec_assoc = models.ContainerSecret()
new_consec_assoc.name = 'private_key'
new_consec_assoc.container_id = container_id
new_consec_assoc.secret_id = private_key_id
container_secret_repo = repos.get_container_secret_repository()
container_secret_repo.create_from(new_consec_assoc)
def modify_certificate_request(order_model, updated_meta):
"""Update the order with CA."""
# TODO(chellygel): Add the modify certificate request logic.
LOG.debug('in modify_certificate_request')
raise NotImplementedError # pragma: no cover
def get_global_preferred_ca():
project = res.get_or_create_global_preferred_project()
preferred_ca_repository = repos.get_preferred_ca_repository()
cas = preferred_ca_repository.get_project_entities(project.id)
if not cas:
return None
else:
return cas[0]
def get_project_preferred_ca_id(project_id):
"""Compute the preferred CA ID for a project
First priority: a preferred CA is defined for the project
Second priority: a preferred CA is defined globally
Else: None
"""
preferred_ca_repository = repos.get_preferred_ca_repository()
cas, offset, limit, total = preferred_ca_repository.get_by_create_date(
project_id=project_id, suppress_exception=True)
if total > 0:
return cas[0].ca_id
global_ca = get_global_preferred_ca()
if global_ca:
return global_ca.ca_id
def _get_ca_id(order_meta, project_id):
ca_id = order_meta.get(cert.CA_ID)
if ca_id:
return ca_id
return get_project_preferred_ca_id(project_id)
def _update_result_follow_on(
result_follow_on,
order_status=None,
retry_task=common.RetryTasks.NO_ACTION_REQUIRED,
retry_msec=common.RETRY_MSEC_DEFAULT):
if order_status:
result_follow_on.status = order_status.id
result_follow_on.status_message = order_status.message
result_follow_on.retry_task = retry_task
if retry_msec and retry_msec >= 0:
result_follow_on.retry_msec = retry_msec
def _get_plugin_meta(order_model):
if order_model:
order_plugin_meta_repo = repos.get_order_plugin_meta_repository()
return order_plugin_meta_repo.get_metadata_for_order(order_model.id)
else:
return {}
def _get_barbican_meta(order_model):
if order_model:
order_barbican_meta_repo = repos.get_order_barbican_meta_repository()
return order_barbican_meta_repo.get_metadata_for_order(order_model.id)
else:
return {}
def _generate_csr_from_private_key(order_model, project_model):
"""Generate a CSR from the private key.
:param: order_model - order for the request
:param: project_model - project for this request
:return: CSR (certificate signing request) in PEM format
:raise: :class:`StoredKeyPrivateKeyNotFound` if private key not found
:class:`StoredKeyContainerNotFound` if container not found
"""
container_id, container = _get_container_from_order_meta(order_model,
project_model)
if not container:
raise excep.StoredKeyContainerNotFound(container_id)
passphrase = None
private_key = None
for cs in container.container_secrets:
secret_repo = repos.get_secret_repository()
if cs.name == 'private_key':
private_key_model = secret_repo.get(
cs.secret_id,
project_model.external_id)
private_key = plugin.get_secret(
'application/pkcs8',
private_key_model,
project_model)
elif cs.name == 'private_key_passphrase':
passphrase_model = secret_repo.get(
cs.secret_id,
project_model.external_id)
passphrase = plugin.get_secret(
'text/plain;charset=utf-8',
passphrase_model,
project_model)
passphrase = str(passphrase)
if not private_key:
raise excep.StoredKeyPrivateKeyNotFound(container.id)
if passphrase is None:
pkey = crypto.load_privatekey(
crypto.FILETYPE_PEM,
private_key
)
else:
pkey = crypto.load_privatekey(
crypto.FILETYPE_PEM,
private_key,
passphrase.encode('utf-8')
)
subject_name = order_model.meta.get('subject_dn')
subject_name_dns = parse_dn(subject_name)
extensions = order_model.meta.get('extensions', None)
req = crypto.X509Req()
subj = req.get_subject()
# Note: must iterate over the DNs in reverse order, or the resulting
# subject name will be reversed.
for ava in reversed(subject_name_dns):
key, val, extra = ava
setattr(subj, key.upper(), val)
req.set_pubkey(pkey)
if extensions:
# TODO(alee-3) We need code here to parse the encoded extensions and
# convert them into X509Extension objects. This code will also be
# used in the validation code. Commenting out for now till we figure
# out how to do this.
# req.add_extensions(extensions)
pass
req.sign(pkey, 'sha256')
csr = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
return csr
def _get_container_from_order_meta(order_model, project_model):
container_ref = order_model.meta.get('container_ref')
# extract container_id as the last part of the URL
container_id = hrefs.get_container_id_from_ref(container_ref)
container_repo = repos.get_container_repository()
container = container_repo.get(container_id,
project_model.external_id,
suppress_exception=True)
return container_id, container
def _notify_ca_unavailable(order_model, result):
"""Notify observer(s) that the CA was unavailable at this time."""
cert.get_event_plugin_manager().notify_ca_is_unavailable(
order_model.project_id,
hrefs.convert_order_to_href(order_model.id),
result.status_message,
result.retry_msec)
def _save_plugin_metadata(order_model, plugin_meta):
"""Add plugin metadata to an order."""
if not isinstance(plugin_meta, dict):
plugin_meta = {}
order_plugin_meta_repo = repos.get_order_plugin_meta_repository()
order_plugin_meta_repo.save(plugin_meta, order_model)
def _save_barbican_metadata(order_model, barbican_meta):
"""Add barbican metadata to an order."""
if not isinstance(barbican_meta, dict):
barbican_meta = {}
order_barbican_meta_repo = repos.get_order_barbican_meta_repository()
order_barbican_meta_repo.save(barbican_meta, order_model)
def _save_secrets(result, project_model, request_type, order_model):
cert_secret_model, transport_key_model = plugin.store_secret(
unencrypted_raw=result.certificate,
content_type_raw='application/octet-stream',
content_encoding='base64',
secret_model=models.Secret(),
project_model=project_model)
# save the certificate chain as a secret.
if result.intermediates:
intermediates_secret_model, transport_key_model = plugin.store_secret(
unencrypted_raw=result.intermediates,
content_type_raw='application/octet-stream',
content_encoding='base64',
secret_model=models.Secret(),
project_model=project_model
)
else:
intermediates_secret_model = None
container_model = models.Container()
container_model.type = "certificate"
container_model.status = models.States.ACTIVE
container_model.project_id = project_model.id
container_repo = repos.get_container_repository()
container_repo.create_from(container_model)
# create container_secret for certificate
new_consec_assoc = models.ContainerSecret()
new_consec_assoc.name = 'certificate'
new_consec_assoc.container_id = container_model.id
new_consec_assoc.secret_id = cert_secret_model.id
container_secret_repo = repos.get_container_secret_repository()
container_secret_repo.create_from(new_consec_assoc)
if intermediates_secret_model:
# create container_secret for intermediate certs
new_consec_assoc = models.ContainerSecret()
new_consec_assoc.name = 'intermediates'
new_consec_assoc.container_id = container_model.id
new_consec_assoc.secret_id = intermediates_secret_model.id
container_secret_repo.create_from(new_consec_assoc)
if request_type == cert.CertificateRequestType.STORED_KEY_REQUEST:
_add_private_key_to_generated_cert_container(container_model.id,
order_model,
project_model)
return container_model

View File

@ -39,16 +39,10 @@ class RetryTasks(object):
NO_ACTION_REQUIRED - To retry/scheduling actions are required NO_ACTION_REQUIRED - To retry/scheduling actions are required
The following task/context-specific actions are available:
INVOKE_CERT_STATUS_CHECK_TASK - Check certificate status later
""" """
INVOKE_SAME_TASK = "Invoke Same Task Again Later" INVOKE_SAME_TASK = "Invoke Same Task Again Later"
NO_ACTION_REQUIRED = "No Retry/Schedule Actions Are Needed" NO_ACTION_REQUIRED = "No Retry/Schedule Actions Are Needed"
INVOKE_CERT_STATUS_CHECK_TASK = "Check Certificate Status Later"
class FollowOnProcessingStatusDTO(object): class FollowOnProcessingStatusDTO(object):

View File

@ -24,7 +24,6 @@ from barbican import i18n as u
from barbican.model import models from barbican.model import models
from barbican.model import repositories as rep from barbican.model import repositories as rep
from barbican.plugin import resources as plugin from barbican.plugin import resources as plugin
from barbican.tasks import certificate_resources as cert
from barbican.tasks import common from barbican.tasks import common
@ -288,13 +287,6 @@ class BeginTypeOrder(BaseTask):
project) project)
order.container_id = new_container.id order.container_id = new_container.id
LOG.debug("...done creating asymmetric order's secret.") LOG.debug("...done creating asymmetric order's secret.")
elif order_type == models.OrderType.CERTIFICATE:
# Request a certificate
new_container = cert.issue_certificate_request(
order, project, result_follow_on)
if new_container:
order.container_id = new_container.id
LOG.debug("...done requesting a certificate.")
else: else:
raise NotImplementedError( raise NotImplementedError(
u._('Order type "{order_type}" not implemented.').format( u._('Order type "{order_type}" not implemented.').format(
@ -310,107 +302,3 @@ class BeginTypeOrder(BaseTask):
def handle_success(self, order, result, *args, **kwargs): def handle_success(self, order, result, *args, **kwargs):
self.helper.handle_success( self.helper.handle_success(
order, result, *args, **kwargs) order, result, *args, **kwargs)
class UpdateOrder(BaseTask):
"""Handles updating an order."""
def get_name(self):
return u._('Update Order')
def __init__(self):
super(UpdateOrder, self).__init__()
LOG.debug('Creating UpdateOrder task processor')
self.helper = _OrderTaskHelper()
def retrieve_entity(self, *args, **kwargs):
return self.helper.retrieve_entity(*args, **kwargs)
def handle_processing(
self, order, order_id, external_project_id, updated_meta):
self.handle_order(order, updated_meta)
def handle_order(self, order, updated_meta):
"""Handle Order Update
:param order: Order to update.
"""
order_info = order.to_dict_fields()
order_type = order_info.get('type')
if order_type == models.OrderType.CERTIFICATE:
# Update a certificate request
cert.modify_certificate_request(order, updated_meta)
LOG.debug("...done updating a certificate order.")
else:
raise NotImplementedError(
u._('Order type "{order_type}" not implemented.').format(
order_type=order_type))
LOG.debug("...done updating order.")
def handle_error(self, order, status, message, exception,
*args, **kwargs):
self.helper.handle_error(
order, status, message, exception, *args, **kwargs)
def handle_success(self, order, result, *args, **kwargs):
self.helper.handle_success(
order, result, *args, **kwargs)
class CheckCertificateStatusOrder(BaseTask):
"""Handles checking the status of a certificate order."""
def get_name(self):
return u._('Check Certificate Order Status')
def __init__(self):
LOG.debug('Creating CheckCertificateStatusOrder task processor')
self.project_repo = rep.get_project_repository()
self.helper = _OrderTaskHelper()
def retrieve_entity(self, *args, **kwargs):
return self.helper.retrieve_entity(*args, **kwargs)
def handle_processing(self, order, *args, **kwargs):
return self.handle_order(order)
def handle_order(self, order):
"""Handle checking the status of a certificate order.
:param order: Order to process.
:return: None if no follow on processing is needed for this task,
otherwise a :class:`FollowOnProcessingStatusDTO` instance
with information on how to process this task into the future.
"""
result_follow_on = common.FollowOnProcessingStatusDTO()
order_info = order.to_dict_fields()
order_type = order_info.get('type')
# Retrieve the project.
project = self.project_repo.get(order.project_id)
if order_type != models.OrderType.CERTIFICATE:
raise NotImplementedError(
u._('Order type "{order_type}" not supported.').format(
order_type=order_type))
# Request a certificate
new_container = cert.check_certificate_request(
order, project, result_follow_on)
if new_container:
order.container_id = new_container.id
LOG.debug("...done checking status of a certificate order.")
return result_follow_on
def handle_error(self, order, status, message, exception,
*args, **kwargs):
self.helper.handle_error(
order, status, message, exception, *args, **kwargs)
def handle_success(self, order, result, *args, **kwargs):
self.helper.handle_success(
order, result, *args, **kwargs)

View File

@ -1,316 +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.
import datetime
from unittest import mock
import testtools
from barbican.common import utils as common_utils
from barbican.model import models
from barbican.plugin.interface import certificate_manager as cm
from barbican.tests import database_utils
from barbican.tests import utils
class WhenTestingCertificateEventPluginManager(testtools.TestCase):
def setUp(self):
super(WhenTestingCertificateEventPluginManager, self).setUp()
self.project_id = '1234'
self.order_ref = 'http://www.mycerts.com/v1/orders/123456'
self.container_ref = 'http://www.mycerts.com/v1/containers/654321'
self.error_msg = 'Something is broken'
self.retry_in_msec = 5432
self.plugin_returned = mock.MagicMock()
self.plugin_name = common_utils.generate_fullname_for(
self.plugin_returned)
self.plugin_loaded = mock.MagicMock(obj=self.plugin_returned)
self.manager = cm.get_event_plugin_manager()
self.manager.extensions = [self.plugin_loaded]
def test_get_plugin_by_name(self):
self.assertEqual(self.plugin_returned,
self.manager.get_plugin_by_name(self.plugin_name))
def test_notify_ca_is_unavailable(self):
self.manager.notify_ca_is_unavailable(
self.project_id,
self.order_ref,
self.error_msg,
self.retry_in_msec)
self.plugin_returned.notify_ca_is_unavailable.assert_called_once_with(
self.project_id,
self.order_ref,
self.error_msg,
self.retry_in_msec)
def test_notify_certificate_is_ready(self):
self.manager.notify_certificate_is_ready(
self.project_id,
self.order_ref,
self.container_ref)
pr = self.plugin_returned
pr.notify_certificate_is_ready.assert_called_once_with(
self.project_id,
self.order_ref,
self.container_ref)
def test_invoke_certificate_plugins(self):
self.manager._invoke_certificate_plugins(
'test_invoke_certificate_plugins',
self.project_id,
self.order_ref,
self.container_ref)
# The _invoke_certificate_plugins method should invoke on
# self.plugin_returned the same method by name as the function
# that invoked it...in this case it is this test method.
pr = self.plugin_returned
pr.test_invoke_certificate_plugins.assert_called_once_with(
self.project_id,
self.order_ref,
self.container_ref)
def test_raises_error_with_no_plugin_by_name_found(self):
self.manager.extensions = []
self.assertRaises(
cm.CertificateEventPluginNotFound,
self.manager.get_plugin_by_name,
'any-name-here'
)
def test_raises_error_with_no_plugin_for_invoke_certificate_plugins(self):
self.manager.extensions = []
self.assertRaises(
cm.CertificateEventPluginNotFound,
self.manager._invoke_certificate_plugins,
self.project_id,
self.order_ref,
self.error_msg,
self.retry_in_msec,
)
class WhenTestingCertificatePluginManager(database_utils.RepositoryTestCase,
utils.MockModelRepositoryMixin):
def setUp(self):
super(WhenTestingCertificatePluginManager, self).setUp()
self.cert_spec = {}
self.plugin_returned = mock.MagicMock()
self.plugin_name = common_utils.generate_fullname_for(
self.plugin_returned)
types_list = [cm.CertificateRequestType.SIMPLE_CMC_REQUEST,
cm.CertificateRequestType.CUSTOM_REQUEST]
self.plugin_returned.supported_request_types.return_value = types_list
self.plugin_returned.supports.return_value = True
self.plugin_loaded = mock.MagicMock(obj=self.plugin_returned)
expiration = (datetime.datetime.utcnow() + datetime.timedelta(
days=cm.CA_INFO_DEFAULT_EXPIRATION_DAYS))
ca_info = {
cm.INFO_NAME: "my_ca",
cm.INFO_DESCRIPTION: "Certificate Authority my_ca",
cm.INFO_CA_SIGNING_CERT: "Undefined",
cm.INFO_INTERMEDIATES: "Undefined",
cm.INFO_EXPIRATION: expiration.isoformat()
}
self.plugin_returned.get_ca_info.return_value = {
'plugin_ca_id1': ca_info
}
parsed_ca = {
'plugin_name': self.plugin_name,
'plugin_ca_id': 'plugin_ca_id1',
'name': self.plugin_name,
'description': 'Master CA for default plugin',
'ca_signing_certificate': 'ZZZZZ',
'intermediates': 'YYYYY'
}
self.ca = models.CertificateAuthority(parsed_ca)
self.ca.id = 'ca_id'
self.ca_repo = mock.MagicMock()
self.ca_repo.get_by_create_date.return_value = (
self.ca, 0, 1, 1)
self.ca_repo.create_from.return_value = None
self.ca_repo.get.return_value = self.ca
self.project = models.Project()
self.project.id = '12345'
self.setup_ca_repository_mock(self.ca_repo)
self.plugin_loaded = mock.MagicMock(obj=self.plugin_returned)
self.manager = cm.CertificatePluginManager()
self.manager.extensions = [self.plugin_loaded]
def test_get_plugin_by_name(self):
self.assertEqual(self.plugin_returned,
self.manager.get_plugin_by_name(self.plugin_name))
def test_get_plugin_by_ca_id(self):
self.assertEqual(self.plugin_returned,
self.manager.get_plugin_by_ca_id('ca_id'))
def test_raises_error_with_no_plugin_by_ca_id_found(self):
self.ca_repo.get.return_value = None
self.assertRaises(
cm.CertificatePluginNotFoundForCAID,
self.manager.get_plugin_by_ca_id,
'any-name-here'
)
def test_raises_error_with_no_plugin_by_name_found(self):
self.manager.extensions = []
self.assertRaises(
cm.CertificatePluginNotFound,
self.manager.get_plugin_by_name,
'any-name-here'
)
def test_get_plugin_no_request_type_provided(self):
# no request_type defaults to "custom"
self.assertEqual(self.plugin_returned,
self.manager.get_plugin(self.cert_spec))
def test_get_plugin_request_type_supported(self):
self.cert_spec = {
cm.REQUEST_TYPE: cm.CertificateRequestType.SIMPLE_CMC_REQUEST}
self.assertEqual(self.plugin_returned,
self.manager.get_plugin(self.cert_spec))
def test_raises_error_get_plugin_request_type_not_supported(self):
self.cert_spec = {
cm.REQUEST_TYPE: cm.CertificateRequestType.FULL_CMC_REQUEST}
self.assertRaises(
cm.CertificatePluginNotFound,
self.manager.get_plugin,
self.cert_spec
)
def test_raises_error_with_no_plugin_found(self):
self.manager.extensions = []
self.assertRaises(
cm.CertificatePluginNotFound,
self.manager.get_plugin,
self.cert_spec
)
def test_get_plugin_with_ca_to_be_added(self):
self.ca_repo.get_by_create_date.return_value = (
None, 0, 1, 0)
self.assertEqual(self.plugin_returned,
self.manager.get_plugin(self.cert_spec))
def test_refresh_ca_list(self):
utc_now = datetime.datetime.utcnow()
expired_time = utc_now - datetime.timedelta(days=1)
expiration = utc_now + datetime.timedelta(days=1)
ca1_info = {
cm.INFO_NAME: "expired_ca_to_be_modified",
cm.INFO_DESCRIPTION: "expired_ca to be modified",
cm.INFO_CA_SIGNING_CERT: "XXXXXXX-expired-XXXXXX",
cm.INFO_INTERMEDIATES: "YYYYYYY-expired-YYYYYYY",
cm.INFO_EXPIRATION: expired_time.isoformat()
}
ca1_modified_info = {
cm.INFO_NAME: "expired_ca_to_be_modified",
cm.INFO_DESCRIPTION: "expired_ca to be modified",
cm.INFO_CA_SIGNING_CERT: "XXXXXXX-no-longer-expired-XXXXXX",
cm.INFO_INTERMEDIATES: "YYYYYYY-no-longer-expired-YYYYYYY",
cm.INFO_EXPIRATION: expiration.isoformat()
}
ca2_info = {
cm.INFO_NAME: "expired_ca_to_be_deleted",
cm.INFO_DESCRIPTION: "expired ca to be deleted",
cm.INFO_CA_SIGNING_CERT: "XXXX-expired-to-be-deleted-XXXX",
cm.INFO_INTERMEDIATES: "YYYY-expired-to-be-deleted-YYYY",
cm.INFO_EXPIRATION: expired_time.isoformat()
}
ca3_info = {
cm.INFO_NAME: "new-ca-to-be-added",
cm.INFO_DESCRIPTION: "new-ca-to-be-added",
cm.INFO_CA_SIGNING_CERT: "XXXX-to-be-addeed-XXXX",
cm.INFO_INTERMEDIATES: "YYYY-to-be-added-YYYY",
cm.INFO_EXPIRATION: expiration.isoformat()
}
self.plugin_returned.get_ca_info.return_value = {
'plugin_ca_id_ca1': ca1_modified_info,
'plugin_ca_id_ca3': ca3_info
}
parsed_ca1 = dict(ca1_info)
parsed_ca1[cm.PLUGIN_CA_ID] = 'plugin_ca_id_ca1'
parsed_ca1['plugin_name'] = self.plugin_name
ca1 = models.CertificateAuthority(parsed_ca1)
ca1.id = "ca1_id"
parsed_ca2 = dict(ca2_info)
parsed_ca2[cm.PLUGIN_CA_ID] = 'plugin_ca_id_ca2'
parsed_ca2['plugin_name'] = self.plugin_name
ca2 = models.CertificateAuthority(parsed_ca2)
ca2.id = "ca2_id"
side_effect = [(None, 0, 4, 0),
([ca1, ca2], 0, 4, 2)]
self.ca_repo.get_by_create_date.side_effect = side_effect
self.manager.refresh_ca_table()
self.plugin_returned.get_ca_info.assert_called_once_with()
self.ca_repo.update_entity.assert_called_once_with(
ca1,
ca1_modified_info)
self.ca_repo.delete_entity_by_id.assert_called_once_with(
ca2.id,
None)
self.ca_repo.create_from.assert_has_calls([])
def test_refresh_ca_list_plugin_when_get_ca_info_raises(self):
self.ca_repo.get_by_create_date.return_value = (None, 0, 4, 0)
self.plugin_returned.get_ca_info.side_effect = Exception()
self.manager.refresh_ca_table()
self.plugin_returned.get_ca_info.assert_called_once_with()
def test_refresh_ca_list_with_bad_ca_returned_from_plugin(self):
ca3_info = {
cm.INFO_DESCRIPTION: "PLUGIN FAIL: this-ca-has-no-info",
}
self.plugin_returned.get_ca_info.return_value = {
'plugin_ca_id_ca3': ca3_info
}
self.ca_repo.get_by_create_date.return_value = (None, 0, 4, 0)
self.ca_repo.create_from.side_effect = Exception()
self.manager.refresh_ca_table()
self.plugin_returned.get_ca_info.assert_called_once_with()
self.ca_repo.create_from.assert_has_calls([])

View File

@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import base64
import datetime
import os import os
import tempfile import tempfile
from unittest import mock from unittest import mock
@ -22,19 +20,14 @@ from unittest import mock
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from requests import exceptions as request_exceptions
import testtools import testtools
from barbican.tests import keys
from barbican.tests import utils from barbican.tests import utils
try: try:
import barbican.plugin.dogtag as dogtag_import import barbican.plugin.dogtag as dogtag_import
import barbican.plugin.interface.certificate_manager as cm
import barbican.plugin.interface.secret_store as sstore import barbican.plugin.interface.secret_store as sstore
import pki
import pki.cert as dogtag_cert
import pki.key as dogtag_key import pki.key as dogtag_key
imports_ok = True imports_ok = True
except ImportError: except ImportError:
@ -276,678 +269,3 @@ class WhenTestingDogtagKRAPlugin(utils.BaseTestCase):
self.assertFalse( self.assertFalse(
self.plugin.generate_supports(key_spec) self.plugin.generate_supports(key_spec)
) )
@testtools.skipIf(not imports_ok, "Dogtag imports not available")
class WhenTestingDogtagCAPlugin(utils.BaseTestCase):
def setUp(self):
super(WhenTestingDogtagCAPlugin, self).setUp()
self.certclient_mock = mock.MagicMock(name="CertClient mock")
self.patcher = mock.patch('pki.crypto.NSSCryptoProvider')
self.patcher2 = mock.patch('pki.client.PKIConnection')
self.patcher.start()
self.patcher2.start()
# create nss db for test only
self.nss_dir = tempfile.mkdtemp()
# create expiration file for test
fh, self.expiration_data_path = tempfile.mkstemp()
exp_time = datetime.datetime.utcnow() + datetime.timedelta(days=2)
os.write(fh, exp_time.strftime(
"%Y-%m-%d %H:%M:%S.%f"))
os.close(fh)
# create host CA file for test
fh, self.host_ca_path = tempfile.mkstemp()
os.write(fh, "host_ca_aid")
os.close(fh)
self.approved_profile_id = "caServerCert"
CONF = dogtag_import.CONF
CONF.dogtag_plugin.nss_db_path = self.nss_dir
CONF.dogtag_plugin.ca_expiration_data_path = self.expiration_data_path
CONF.dogtag_plugin.ca_host_aid_path = self.host_ca_path
CONF.dogtag_plugin.auto_approved_profiles = [self.approved_profile_id]
CONF.dogtag_plugin.dogtag_host = "localhost"
CONF.dogtag_plugin.dogtag_port = 8443
CONF.dogtag_plugin.simple_cmc_profile = "caOtherCert"
self.cfg = CONF
self.plugin = dogtag_import.DogtagCAPlugin(CONF)
self.plugin.certclient = self.certclient_mock
self.order_id = mock.MagicMock()
self.profile_id = mock.MagicMock()
# request generated
self.request_id_mock = mock.MagicMock()
self.request = dogtag_cert.CertRequestInfo()
self.request.request_id = self.request_id_mock
self.request.request_status = dogtag_cert.CertRequestStatus.COMPLETE
self.cert_id_mock = mock.MagicMock()
self.request.cert_id = self.cert_id_mock
# cert generated
self.cert = mock.MagicMock()
self.cert.encoded = keys.get_certificate_pem()
self.cert.pkcs7_cert_chain = keys.get_certificate_der()
# for cancel/modify
self.review_response = mock.MagicMock()
# modified request
self.modified_request = mock.MagicMock()
self.modified_request_id_mock = mock.MagicMock()
self.modified_request.request_id = self.modified_request_id_mock
self.modified_request.request_status = (
dogtag_cert.CertRequestStatus.COMPLETE)
self.modified_request.cert_id = self.cert_id_mock
self.barbican_meta_dto = cm.BarbicanMetaDTO()
def tearDown(self):
super(WhenTestingDogtagCAPlugin, self).tearDown()
self.patcher2.stop()
self.patcher.stop()
os.rmdir(self.nss_dir)
os.remove(self.host_ca_path)
os.remove(self.expiration_data_path)
def _process_approved_profile_request(self, order_meta, plugin_meta):
enrollment_result = dogtag_cert.CertEnrollmentResult(
self.request, self.cert)
enrollment_results = [enrollment_result]
self.certclient_mock.enroll_cert.return_value = enrollment_results
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.enroll_cert.assert_called_once_with(
self.approved_profile_id,
order_meta)
self.assertEqual(cm.CertificateStatus.CERTIFICATE_GENERATED,
result_dto.status,
"result_dto status incorrect")
self.assertEqual(base64.b64encode(keys.get_certificate_pem()),
result_dto.certificate)
self.assertEqual(
self.request_id_mock,
plugin_meta.get(dogtag_import.DogtagCAPlugin.REQUEST_ID)
)
def _process_non_approved_profile_request(self, order_meta, plugin_meta,
profile_id, inputs=None):
if inputs is None:
inputs = {
'cert_request_type': 'pkcs10',
'cert_request': base64.b64decode(
order_meta.get('request_data'))
}
# mock CertRequestInfo
enrollment_result = dogtag_cert.CertRequestInfo()
enrollment_result.request_id = self.request_id_mock
enrollment_result.request_status = (
dogtag_cert.CertRequestStatus.PENDING)
# mock CertRequestInfoCollection
enrollment_results = dogtag_cert.CertRequestInfoCollection()
enrollment_results.cert_request_info_list = (
[enrollment_result])
self.certclient_mock.create_enrollment_request.return_value = (
enrollment_result)
self.certclient_mock.submit_enrollment_request.return_value = (
enrollment_results)
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.create_enrollment_request.assert_called_once_with(
profile_id, inputs)
self.certclient_mock.submit_enrollment_request.assert_called_once_with(
enrollment_result)
self.assertEqual(cm.CertificateStatus.WAITING_FOR_CA,
result_dto.status,
"result_dto status incorrect")
self.assertEqual(
self.request_id_mock,
plugin_meta.get(dogtag_import.DogtagCAPlugin.REQUEST_ID)
)
def test_issue_simple_cmc_request(self):
order_meta = {
cm.REQUEST_TYPE: cm.CertificateRequestType.SIMPLE_CMC_REQUEST,
'request_data': base64.b64encode(keys.get_csr_pem())
}
plugin_meta = {}
self._process_non_approved_profile_request(
order_meta,
plugin_meta,
self.cfg.dogtag_plugin.simple_cmc_profile)
def test_issue_full_cmc_request(self):
order_meta = {
cm.REQUEST_TYPE: cm.CertificateRequestType.FULL_CMC_REQUEST,
'request_data': 'Full CMC data ...'
}
plugin_meta = {}
self.assertRaises(
dogtag_import.DogtagPluginNotSupportedException,
self.plugin.issue_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
def test_issue_stored_key_request(self):
order_meta = {
cm.REQUEST_TYPE: cm.CertificateRequestType.STORED_KEY_REQUEST,
'request_data': base64.b64encode(keys.get_csr_pem())
}
plugin_meta = {}
self._process_non_approved_profile_request(
order_meta,
plugin_meta,
self.cfg.dogtag_plugin.simple_cmc_profile)
def test_issue_custom_key_request(self):
order_meta = {
cm.REQUEST_TYPE: cm.CertificateRequestType.CUSTOM_REQUEST,
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id,
}
plugin_meta = {}
self._process_approved_profile_request(order_meta, plugin_meta)
def test_issue_no_cert_request_type_provided(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self._process_approved_profile_request(order_meta, plugin_meta)
def test_issue_bad_cert_request_type_provided(self):
order_meta = {
cm.REQUEST_TYPE: 'BAD_REQUEST_TYPE',
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.profile_id,
}
plugin_meta = {}
self.assertRaises(
dogtag_import.DogtagPluginNotSupportedException,
self.plugin.issue_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
def test_issue_return_data_error_with_no_profile_id(self):
order_meta = {}
plugin_meta = {}
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.assertEqual(result_dto.status,
cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
"result_dto status incorrect")
self.assertEqual(result_dto.status_message,
"No profile_id specified")
def test_issue_return_data_error_with_request_rejected(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self.request.request_status = dogtag_cert.CertRequestStatus.REJECTED
enrollment_result = dogtag_cert.CertEnrollmentResult(
self.request, None)
enrollment_results = [enrollment_result]
self.certclient_mock.enroll_cert.return_value = enrollment_results
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.enroll_cert.assert_called_once_with(
self.approved_profile_id,
order_meta)
self.assertEqual(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
result_dto.status,
"result_dto status incorrect")
self.assertEqual(
self.request_id_mock,
plugin_meta.get(dogtag_import.DogtagCAPlugin.REQUEST_ID))
def test_issue_return_canceled_with_request_canceled(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self.request.request_status = dogtag_cert.CertRequestStatus.CANCELED
enrollment_result = dogtag_cert.CertEnrollmentResult(
self.request, None)
enrollment_results = [enrollment_result]
self.certclient_mock.enroll_cert.return_value = enrollment_results
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.enroll_cert.assert_called_once_with(
self.approved_profile_id,
order_meta)
self.assertEqual(cm.CertificateStatus.REQUEST_CANCELED,
result_dto.status,
"result_dto status incorrect")
self.assertEqual(
self.request_id_mock,
plugin_meta.get(dogtag_import.DogtagCAPlugin.REQUEST_ID),
)
def test_issue_return_waiting_with_request_pending(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: "otherProfile",
'cert_request': base64.b64encode(keys.get_csr_pem())}
plugin_meta = {}
inputs = {
'cert_request': keys.get_csr_pem(),
dogtag_import.DogtagCAPlugin.PROFILE_ID: "otherProfile"
}
self._process_non_approved_profile_request(
order_meta, plugin_meta, "otherProfile", inputs)
def test_issue_raises_error_request_complete_no_cert(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
enrollment_result = dogtag_cert.CertEnrollmentResult(
self.request, None)
enrollment_results = [enrollment_result]
self.certclient_mock.enroll_cert.return_value = enrollment_results
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.issue_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
self.assertEqual(
self.request_id_mock,
plugin_meta.get(dogtag_import.DogtagCAPlugin.REQUEST_ID)
)
def test_issue_raises_error_request_unknown_status(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self.request.request_status = "unknown_status"
enrollment_result = dogtag_cert.CertEnrollmentResult(
self.request, None)
enrollment_results = [enrollment_result]
self.certclient_mock.enroll_cert.return_value = enrollment_results
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.issue_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
self.assertEqual(
self.request_id_mock,
plugin_meta.get(dogtag_import.DogtagCAPlugin.REQUEST_ID)
)
def test_issue_return_client_error_bad_request_exception(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self.certclient_mock.enroll_cert.side_effect = (
pki.BadRequestException("bad request"))
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.enroll_cert.assert_called_once_with(
self.approved_profile_id,
order_meta)
self.assertEqual(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
result_dto.status,
"result_dto status incorrect")
def test_issue_raises_error_pki_exception(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self.certclient_mock.enroll_cert.side_effect = (
pki.PKIException("generic enrollment error"))
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.issue_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
def test_issue_return_ca_unavailable(self):
order_meta = {
dogtag_import.DogtagCAPlugin.PROFILE_ID: self.approved_profile_id}
plugin_meta = {}
self.certclient_mock.enroll_cert.side_effect = (
request_exceptions.RequestException())
result_dto = self.plugin.issue_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.enroll_cert.assert_called_once_with(
self.approved_profile_id,
order_meta)
self.assertEqual(cm.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST,
result_dto.status,
"result_dto status incorrect")
def test_cancel_request(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.cancel_request.return_value = None
self.certclient_mock.review_request.return_value = self.review_response
result_dto = self.plugin.cancel_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.cancel_request.assert_called_once_with(
self.request_id_mock,
self.review_response)
self.assertEqual(cm.CertificateStatus.REQUEST_CANCELED,
result_dto.status,
"result_dto_status incorrect")
def test_cancel_no_request_found(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.review_request.side_effect = (
pki.RequestNotFoundException("request_not_found"))
result_dto = self.plugin.cancel_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.review_request.assert_called_once_with(
self.request_id_mock)
self.assertEqual(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
result_dto.status,
"result_dto_status incorrect")
def test_cancel_conflicting_operation(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.review_request.return_value = self.review_response
self.certclient_mock.cancel_request.side_effect = (
pki.ConflictingOperationException("conflicting_operation"))
result_dto = self.plugin.cancel_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.cancel_request.assert_called_once_with(
self.request_id_mock,
self.review_response)
self.assertEqual(cm.CertificateStatus.INVALID_OPERATION,
result_dto.status,
"result_dto_status incorrect")
def test_cancel_ca_unavailable(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.review_request.side_effect = (
request_exceptions.RequestException("request_exception"))
result_dto = self.plugin.cancel_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.assertEqual(cm.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST,
result_dto.status,
"result_dto_status incorrect")
def test_cancel_raise_error_no_request_id(self):
order_meta = mock.ANY
plugin_meta = {}
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.cancel_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
def test_check_status(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.get_request.return_value = self.request
self.certclient_mock.get_cert.return_value = self.cert
result_dto = self.plugin.check_certificate_status(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.get_request.assert_called_once_with(
self.request_id_mock)
self.certclient_mock.get_cert.assert_called_once_with(
self.cert_id_mock)
self.assertEqual(cm.CertificateStatus.CERTIFICATE_GENERATED,
result_dto.status,
"result_dto_status incorrect")
self.assertEqual(keys.get_certificate_pem(),
result_dto.certificate)
def test_check_status_raise_error_no_request_id(self):
order_meta = mock.ANY
plugin_meta = {}
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.check_certificate_status,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
def test_check_status_rejected(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.request.request_status = dogtag_cert.CertRequestStatus.REJECTED
self.certclient_mock.get_request.return_value = self.request
result_dto = self.plugin.check_certificate_status(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.get_request.assert_called_once_with(
self.request_id_mock)
self.assertEqual(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
result_dto.status,
"result_dto_status incorrect")
self.assertIsNone(result_dto.certificate)
def test_check_status_canceled(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.request.request_status = dogtag_cert.CertRequestStatus.CANCELED
self.certclient_mock.get_request.return_value = self.request
result_dto = self.plugin.check_certificate_status(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.get_request.assert_called_once_with(
self.request_id_mock)
self.assertEqual(cm.CertificateStatus.REQUEST_CANCELED,
result_dto.status,
"result_dto_status incorrect")
self.assertIsNone(result_dto.certificate)
def test_check_status_pending(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.request.request_status = dogtag_cert.CertRequestStatus.PENDING
self.certclient_mock.get_request.return_value = self.request
result_dto = self.plugin.check_certificate_status(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.get_request.assert_called_once_with(
self.request_id_mock)
self.assertEqual(cm.CertificateStatus.WAITING_FOR_CA,
result_dto.status,
"result_dto_status incorrect")
self.assertIsNone(result_dto.certificate)
def test_check_status_raises_error_complete_no_cert(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.get_request.return_value = self.request
self.certclient_mock.get_cert.return_value = None
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.check_certificate_status,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)
def test_modify_request(self):
order_meta = {
cm.REQUEST_TYPE: cm.CertificateRequestType.SIMPLE_CMC_REQUEST,
'request_data': base64.b64encode(keys.get_csr_pem())
}
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self._process_non_approved_profile_request(
order_meta,
plugin_meta,
self.cfg.dogtag_plugin.simple_cmc_profile)
self.certclient_mock.cancel_request.return_value = None
self.certclient_mock.review_request.return_value = self.review_response
result_dto = self.plugin.modify_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.cancel_request.assert_called_once_with(
self.request_id_mock,
self.review_response)
self.assertEqual(cm.CertificateStatus.WAITING_FOR_CA,
result_dto.status,
"result_dto_status incorrect")
def test_modify_no_request_found(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.review_request.side_effect = (
pki.RequestNotFoundException("request_not_found"))
result_dto = self.plugin.modify_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.review_request.assert_called_once_with(
self.request_id_mock)
self.assertEqual(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
result_dto.status,
"result_dto_status incorrect")
def test_modify_conflicting_operation(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.review_request.return_value = self.review_response
self.certclient_mock.cancel_request.side_effect = (
pki.ConflictingOperationException("conflicting_operation"))
result_dto = self.plugin.modify_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.certclient_mock.cancel_request.assert_called_once_with(
self.request_id_mock,
self.review_response)
self.assertEqual(cm.CertificateStatus.INVALID_OPERATION,
result_dto.status,
"result_dto_status incorrect")
def test_modify_ca_unavailable(self):
order_meta = mock.ANY
plugin_meta = {dogtag_import.DogtagCAPlugin.REQUEST_ID:
self.request_id_mock}
self.certclient_mock.review_request.side_effect = (
request_exceptions.RequestException("request_exception"))
result_dto = self.plugin.modify_certificate_request(
self.order_id, order_meta, plugin_meta, self.barbican_meta_dto)
self.assertEqual(cm.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST,
result_dto.status,
"result_dto_status incorrect")
def test_modify_raise_error_no_request_id(self):
order_meta = mock.ANY
plugin_meta = {}
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.modify_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto
)

View File

@ -1,83 +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.
import testtools
import barbican.plugin.interface.certificate_manager as cm
import barbican.plugin.simple_certificate_manager as simple
class WhenTestingSimpleCertificateManagerPlugin(testtools.TestCase):
def setUp(self):
super(WhenTestingSimpleCertificateManagerPlugin, self).setUp()
self.plugin = simple.SimpleCertificatePlugin()
def test_issue_certificate_request(self):
result = self.plugin.issue_certificate_request(None, None, None, None)
self.assertEqual(cm.CertificateStatus.WAITING_FOR_CA, result.status)
def test_check_certificate_status(self):
result = self.plugin.check_certificate_status(None, None, None, None)
self.assertEqual(
cm.CertificateStatus.CERTIFICATE_GENERATED, result.status)
def test_modify_certificate_request(self):
result = self.plugin.modify_certificate_request(None, None, None, None)
self.assertEqual(cm.CertificateStatus.WAITING_FOR_CA, result.status)
def test_cancel_certificate_request(self):
result = self.plugin.cancel_certificate_request(None, None, None, None)
self.assertEqual(cm.CertificateStatus.REQUEST_CANCELED, result.status)
def test_supports(self):
result = self.plugin.supports(None)
self.assertTrue(result)
def test_get_ca_info(self):
result = self.plugin.get_ca_info()
name = self.plugin.get_default_ca_name()
self.assertIn(name, result)
self.assertEqual(name, result[name][cm.INFO_NAME])
self.assertEqual(self.plugin.get_default_signing_cert(),
result[name][cm.INFO_CA_SIGNING_CERT])
def test_supported_request_types(self):
result = self.plugin.supported_request_types()
supported_list = [cm.CertificateRequestType.CUSTOM_REQUEST,
cm.CertificateRequestType.SIMPLE_CMC_REQUEST,
cm.CertificateRequestType.FULL_CMC_REQUEST,
cm.CertificateRequestType.STORED_KEY_REQUEST]
self.assertEqual(supported_list, result)
class WhenTestingSimpleCertificateEventManagerPlugin(testtools.TestCase):
def setUp(self):
super(WhenTestingSimpleCertificateEventManagerPlugin, self).setUp()
self.plugin = simple.SimpleCertificateEventPlugin()
def test_notify_ca_is_unavailable(self):
# Test that eventing plugin method does not have side effects such as
# raising exceptions.
self.plugin.notify_ca_is_unavailable(None, None, None, None)
def test_notify_certificate_is_ready(self):
# Test that eventing plugin method does not have side effects such as
# raising exceptions.
self.plugin.notify_certificate_is_ready(None, None, None)

View File

@ -1,453 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# 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.
import base64
import os
from unittest import mock
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import pkcs7
from cryptography import x509
import fixtures
from OpenSSL import crypto
from oslo_config import fixture as oslo_fixture
import barbican.plugin.interface.certificate_manager as cm
from barbican.plugin import snakeoil_ca
from barbican.tests import certificate_utils
from barbican.tests import utils
class BaseTestCase(utils.BaseTestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
self.conf = self.useFixture(oslo_fixture.Config(
conf=snakeoil_ca.CONF)).conf
self.tmp_dir = self.useFixture(fixtures.TempDir()).path
def tearDown(self):
super(BaseTestCase, self).tearDown()
class CaTestCase(BaseTestCase):
def test_gen_cacert_no_file_storage(self):
subject_dn = (
'cn=Test CN,o=Test O,L=Test L,st=Test ST,ou=Test OU'
)
ca = snakeoil_ca.SnakeoilCA(cert_path=None, key_path=None,
key_size=512, subject_dn=subject_dn)
subject = ca.cert.get_subject()
self.assertIsNotNone(ca.key)
self.assertEqual("Test ST", subject.ST)
self.assertEqual("Test L", subject.L)
self.assertEqual("Test O", subject.O)
self.assertEqual("Test CN", subject.CN)
self.assertEqual("Test OU", subject.OU)
self.assertEqual(
ca.chain,
crypto.dump_certificate(crypto.FILETYPE_PEM, ca.cert))
def test_gen_cacert_with_file_storage(self):
cert_path = self.tmp_dir + 'cert.pem'
key_path = self.tmp_dir + 'key.pem'
chain_path = self.tmp_dir + 'cert.chain'
pkcs7_path = self.tmp_dir + 'cert.p7b'
subject_dn = 'cn=Test CN,o=Test O,L=Test L,st=Test ST'
ca = snakeoil_ca.SnakeoilCA(
cert_path=cert_path,
key_path=key_path,
chain_path=chain_path,
pkcs7_path=pkcs7_path,
key_size=2048,
subject_dn=subject_dn)
subject = ca.cert.get_subject()
self.assertEqual(
ca.chain,
crypto.dump_certificate(crypto.FILETYPE_PEM, ca.cert))
self.assertIsNotNone(ca.key)
self.assertEqual("Test ST", subject.ST)
self.assertEqual("Test L", subject.L)
self.assertEqual("Test O", subject.O)
self.assertEqual("Test CN", subject.CN)
# Make sure we preserve existing keypairs
ca = snakeoil_ca.SnakeoilCA(
cert_path=cert_path,
key_path=key_path,
chain_path=chain_path,
pkcs7_path=pkcs7_path
)
subject = ca.cert.get_subject()
self.assertEqual("Test ST", subject.ST)
self.assertEqual("Test L", subject.L)
self.assertEqual("Test O", subject.O)
self.assertEqual("Test CN", subject.CN)
def test_gen_sub_cacert_with_file_storage(self):
cert_path = self.tmp_dir + 'cert.pem'
key_path = self.tmp_dir + 'key.pem'
chain_path = self.tmp_dir + 'cert.chain'
pkcs7_path = self.tmp_dir + 'cert.p7b'
subject_dn = 'cn=Test CN,o=Test O,L=Test L,st=Test ST'
parent_ca = snakeoil_ca.SnakeoilCA(
cert_path=cert_path,
key_path=key_path,
chain_path=chain_path,
pkcs7_path=pkcs7_path,
key_size=2048,
subject_dn=subject_dn)
self.assertIsNotNone(parent_ca)
# create a sub-ca
subject_dn = 'cn=Sub CA Test CN,o=Test O,L=Test L,st=Test ST'
cert_path = self.tmp_dir + 'sub_cert.pem'
key_path = self.tmp_dir + 'sub_key.pem'
chain_path = self.tmp_dir + 'sub_cert.chain'
pkcs7_path = self.tmp_dir + 'sub_cert.p7b'
sub_ca = snakeoil_ca.SnakeoilCA(
cert_path=cert_path,
key_path=key_path,
chain_path=chain_path,
pkcs7_path=pkcs7_path,
key_size=2048,
subject_dn=subject_dn,
parent_chain_path=parent_ca.chain_path,
signing_dn=parent_ca.subject_dn,
signing_key=parent_ca.key
)
subject = sub_ca.cert.get_subject()
self.assertEqual("Test ST", subject.ST)
self.assertEqual("Test L", subject.L)
self.assertEqual("Test O", subject.O)
self.assertEqual("Sub CA Test CN", subject.CN)
class CertManagerTestCase(BaseTestCase):
def setUp(self):
super(CertManagerTestCase, self).setUp()
subject_dn = 'cn=Test CN,o=Test O,L=Test L,st=Test ST'
self.ca = snakeoil_ca.SnakeoilCA(cert_path=None, key_path=None,
key_size=512, subject_dn=subject_dn)
def verify_sig(self, encoded_cert):
cert = x509.load_der_x509_certificate(encoded_cert, default_backend())
crypto.verify(
self.ca.cert,
cert.signature,
cert.tbs_certificate_bytes,
'sha256')
def test_gen_cert_no_file_storage(self):
req = certificate_utils.get_valid_csr_object()
cm = snakeoil_ca.CertManager(self.ca)
cert = cm.make_certificate(req)
first_serial = cert.get_serial_number()
cert_enc = crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)
self.verify_sig(cert_enc)
cert = cm.make_certificate(req)
self.assertNotEqual(first_serial, cert.get_serial_number())
self.verify_sig(cert_enc)
cm = snakeoil_ca.CertManager(self.ca)
cert = cm.make_certificate(req)
def test_gen_cert_with_file_storage(self):
req = certificate_utils.get_valid_csr_object()
cm = snakeoil_ca.CertManager(self.ca)
cert = cm.make_certificate(req)
cert_enc = crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)
first_serial = cert.get_serial_number()
self.verify_sig(cert_enc)
cm = snakeoil_ca.CertManager(self.ca)
cert = cm.make_certificate(req)
self.assertNotEqual(first_serial, cert.get_serial_number())
class SnakeoilCAPluginTestCase(BaseTestCase):
def setUp(self):
super(SnakeoilCAPluginTestCase, self).setUp()
self.ca_cert_path = os.path.join(self.tmp_dir, 'ca.cert')
self.ca_key_path = os.path.join(self.tmp_dir, 'ca.key')
self.ca_chain_path = os.path.join(self.tmp_dir, 'ca.chain')
self.ca_pkcs7_path = os.path.join(self.tmp_dir, 'ca.pkcs7')
self.db_dir = self.tmp_dir
self.conf.snakeoil_ca_plugin.subca_cert_key_directory = os.path.join(
self.tmp_dir, 'subca_cert_key_dir')
self.subca_cert_key_directory = (
self.conf.snakeoil_ca_plugin.subca_cert_key_directory)
self.plugin = snakeoil_ca.SnakeoilCACertificatePlugin(
self.conf)
self.order_id = mock.MagicMock()
self.barbican_meta_dto = cm.BarbicanMetaDTO()
def test_issue_certificate_request(self):
req = certificate_utils.get_valid_csr_object()
req_enc = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
req_enc = base64.b64encode(req_enc)
order_meta = {'request_data': req_enc}
resp = self.plugin.issue_certificate_request(self.order_id,
order_meta, {},
self.barbican_meta_dto)
crypto.load_certificate(
crypto.FILETYPE_PEM, base64.b64decode(resp.certificate))
def test_issue_certificate_request_with_ca_id(self):
req = certificate_utils.get_valid_csr_object()
req_enc = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
req_enc = base64.b64encode(req_enc)
order_meta = {'request_data': req_enc}
plugin_meta = {'plugin_ca_id': self.plugin.get_default_ca_name()}
self.barbican_meta_dto.plugin_ca_id = self.plugin.get_default_ca_name()
resp = self.plugin.issue_certificate_request(self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto)
crypto.load_certificate(
crypto.FILETYPE_PEM, base64.b64decode(resp.certificate))
def test_issue_raises_with_invalid_ca_id(self):
req = certificate_utils.get_valid_csr_object()
req_enc = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
req_enc = base64.b64encode(req_enc)
order_meta = {'request_data': req_enc}
plugin_meta = {'plugin_ca_id': "invalid_ca_id"}
self.barbican_meta_dto.plugin_ca_id = "invalid_ca_id"
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.issue_certificate_request,
self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto)
def test_issue_certificate_request_set_subject(self):
req = certificate_utils.get_valid_csr_object()
subj = req.get_subject()
subj.countryName = 'US'
subj.stateOrProvinceName = 'OR'
subj.localityName = 'Testlandia'
subj.organizationName = 'Testers Anon'
subj.organizationalUnitName = 'Testers OU'
subj.commonName = 'Testing'
req_enc = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
req_enc = base64.b64encode(req_enc)
order_meta = {'request_data': req_enc}
resp = self.plugin.issue_certificate_request(self.order_id,
order_meta, {},
self.barbican_meta_dto)
cert = crypto.load_certificate(
crypto.FILETYPE_PEM, base64.b64decode(resp.certificate))
cert_subj = cert.get_subject()
self.assertEqual('US', cert_subj.C)
self.assertEqual('OR', cert_subj.ST)
self.assertEqual('Testlandia', cert_subj.L)
self.assertEqual('Testers Anon', cert_subj.O)
self.assertEqual('Testers OU', cert_subj.OU)
self.assertEqual('Testing', cert_subj.CN)
def test_issue_certificate_request_stored_key(self):
req = certificate_utils.get_valid_csr_object()
req_enc = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
self.barbican_meta_dto.generated_csr = req_enc
resp = self.plugin.issue_certificate_request(
self.order_id, {}, {}, self.barbican_meta_dto)
crypto.load_certificate(
crypto.FILETYPE_PEM, base64.b64decode(resp.certificate))
def test_no_request_data(self):
res = self.plugin.issue_certificate_request(
self.order_id, {}, {}, self.barbican_meta_dto)
self.assertIs(cm.CertificateStatus.CLIENT_DATA_ISSUE_SEEN,
res.status)
self.assertEqual("No request_data specified", res.status_message)
def test_get_default_ca_name(self):
self.assertEqual("Snakeoil CA", self.plugin.get_default_ca_name())
def test_get_default_signing_cert(self):
ca_cert = self.plugin.get_default_signing_cert()
self.assertEqual(
crypto.dump_certificate(crypto.FILETYPE_PEM, self.plugin.ca.cert),
ca_cert)
def test_get_default_intermediates_none(self):
intermediates = self.plugin.get_default_intermediates()
self.assertIsNone(intermediates)
def test_not_implemented(self):
self.assertRaises(NotImplementedError,
self.plugin.modify_certificate_request,
'', {}, {}, {})
self.assertRaises(NotImplementedError,
self.plugin.cancel_certificate_request,
'', {}, {}, {})
self.assertRaises(NotImplementedError,
self.plugin.check_certificate_status,
'', {}, {}, {})
def test_support_request_types(self):
manager = cm.CertificatePluginManager()
manager.extensions = [mock.MagicMock(obj=self.plugin)]
cert_spec = {
cm.REQUEST_TYPE: cm.CertificateRequestType.CUSTOM_REQUEST}
self.assertEqual(self.plugin, manager.get_plugin(cert_spec))
self.assertTrue(self.plugin.supports(cert_spec))
cert_spec = {
cm.REQUEST_TYPE: cm.CertificateRequestType.STORED_KEY_REQUEST}
self.assertEqual(self.plugin, manager.get_plugin(cert_spec))
self.assertTrue(self.plugin.supports(cert_spec))
cert_spec = {
cm.REQUEST_TYPE: cm.CertificateRequestType.FULL_CMC_REQUEST}
self.assertRaises(cm.CertificatePluginNotFound,
manager.get_plugin, cert_spec)
self.assertFalse(self.plugin.supports(cert_spec))
def test_supports_create_ca(self):
self.assertTrue(self.plugin.supports_create_ca())
def _create_subca(self):
create_ca_dto = cm.CACreateDTO(
name="sub ca1",
description="subordinate ca",
subject_dn="cn=subordinate ca signing cert, o=example.com",
parent_ca_id=self.plugin.get_default_ca_name()
)
return self.plugin.create_ca(create_ca_dto)
def test_create_ca(self):
subca_dict = self._create_subca()
self.assertEqual("sub ca1", subca_dict.get(cm.INFO_NAME))
self.assertIsNotNone(subca_dict.get(cm.INFO_EXPIRATION))
self.assertIsNotNone(subca_dict.get(cm.PLUGIN_CA_ID))
ca_cert = subca_dict.get(cm.INFO_CA_SIGNING_CERT)
self.assertIsNotNone(ca_cert)
intermediates = subca_dict.get(cm.INFO_INTERMEDIATES)
self.assertIsNotNone(intermediates)
cacert = crypto.load_certificate(crypto.FILETYPE_PEM, ca_cert)
subject = cacert.get_subject()
self.assertEqual(
"subordinate ca signing cert",
subject.CN)
certs = pkcs7.load_pem_pkcs7_certificates(intermediates)
self.assertIsNotNone(certs[0].signature)
# TODO(alee) Verify that ca cert is signed by parent CA
def test_issue_certificate_request_with_subca_id(self):
subca_dict = self._create_subca()
req = certificate_utils.get_valid_csr_object()
req_enc = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
req_enc = base64.b64encode(req_enc)
order_meta = {'request_data': req_enc}
plugin_meta = {'plugin_ca_id': subca_dict.get(cm.PLUGIN_CA_ID)}
self.barbican_meta_dto.plugin_ca_id = subca_dict.get(cm.PLUGIN_CA_ID)
resp = self.plugin.issue_certificate_request(self.order_id,
order_meta,
plugin_meta,
self.barbican_meta_dto)
new_cert = crypto.load_certificate(
crypto.FILETYPE_PEM, base64.b64decode(resp.certificate))
signing_cert = crypto.load_certificate(
crypto.FILETYPE_PEM, subca_dict['ca_signing_certificate'])
self.assertEqual(signing_cert.get_subject(), new_cert.get_issuer())
def test_delete_ca(self):
subca_dict = self._create_subca()
ca_id = subca_dict.get(cm.PLUGIN_CA_ID)
self.assertIsNotNone(ca_id)
cert_path = os.path.join(self.subca_cert_key_directory,
ca_id + ".cert")
key_path = os.path.join(self.subca_cert_key_directory,
ca_id + ".key")
self.assertTrue(os.path.exists(cert_path))
self.assertTrue(os.path.exists(key_path))
self.plugin.delete_ca(ca_id)
self.assertFalse(os.path.exists(cert_path))
self.assertFalse(os.path.exists(key_path))
cas = self.plugin.get_ca_info()
self.assertNotIn(ca_id, cas.keys())
def test_raises_no_parent_id_passed_in(self):
create_ca_dto = cm.CACreateDTO(
name="sub ca1",
description="subordinate ca",
subject_dn="cn=subordinate ca signing cert, o=example.com",
)
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.create_ca,
create_ca_dto
)
def test_raises_invalid_parent_id_passed_in(self):
create_ca_dto = cm.CACreateDTO(
name="sub ca1",
description="subordinate ca",
subject_dn="cn=subordinate ca signing cert, o=example.com",
parent_ca_id="foo"
)
self.assertRaises(
cm.CertificateGeneralException,
self.plugin.create_ca,
create_ca_dto
)
def test_get_ca_info(self):
ca_info = self.plugin.get_ca_info()
ca_dict = ca_info.get(self.plugin.ca.name)
self.assertIsNotNone(ca_dict)
self.assertEqual(self.plugin.ca.name, ca_dict.get(cm.INFO_NAME))
self.assertIsNotNone(ca_dict.get(cm.INFO_CA_SIGNING_CERT))
self.assertEqual(str, type(ca_dict.get(cm.INFO_EXPIRATION)))
def test_get_ca_info_with_subca(self):
subca_dict = self._create_subca()
subca_id = subca_dict.get(cm.PLUGIN_CA_ID)
ca_info = self.plugin.get_ca_info()
self.assertIn(subca_id, ca_info.keys())
self.assertIn(self.plugin.get_default_ca_name(), ca_info.keys())
self.assertEqual(str, type(subca_dict.get(cm.INFO_EXPIRATION)))

View File

@ -1,111 +0,0 @@
# Copyright (c) 2013-2014 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 testtools
from unittest import mock
try:
import barbican.plugin.interface.certificate_manager as cm
import barbican.plugin.symantec as sym
imports_ok = True
except ImportError:
# Symantec imports probably not available
imports_ok = False
from barbican.tests import utils
@testtools.skipIf(not imports_ok, "Symantec imports not available")
class WhenTestingSymantecPlugin(utils.BaseTestCase):
def setUp(self):
super(WhenTestingSymantecPlugin, self).setUp()
self.order_meta = {
'cert_type': 'ssl123',
'organization': 'Shinra Corp',
'phone': '555-555-5555',
'so many things...': 'more...'
}
self.error_msg = 'Error Message Here'
self.symantec = sym.SymantecCertificatePlugin()
self.barbican_plugin_dto = cm.BarbicanMetaDTO()
self.symantec_patcher = mock.patch(
'barbican.plugin.symantec._ca_create_order'
)
self.mock_create_order = self.symantec_patcher.start()
def tearDown(self):
super(WhenTestingSymantecPlugin, self).tearDown()
if hasattr(self, 'mock_create_order'):
self.mock_create_order.stop()
def test_successful_issue_certificate_request(self):
self.mock_create_order.return_value = (True, None, None)
order_id = '1234'
plugin_meta = {}
result = self.symantec.issue_certificate_request(
order_id,
self.order_meta,
plugin_meta,
self.barbican_plugin_dto
)
self.assertEqual("waiting for CA", result.status)
def test_unsuccessful_certificate_request_can_retry(self):
self.mock_create_order.return_value = (False, self.error_msg, True)
order_id = '1234'
plugin_meta = {}
result = self.symantec.issue_certificate_request(
order_id,
self.order_meta,
plugin_meta,
self.barbican_plugin_dto
)
self.assertEqual("client data issue seen", result.status)
def test_unsuccessful_certificate_request_no_retry(self):
self.mock_create_order.return_value = (False, self.error_msg, False)
order_id = '12345'
plugin_meta = {}
result = self.symantec.issue_certificate_request(
order_id,
self.order_meta,
plugin_meta,
self.barbican_plugin_dto
)
self.assertEqual("CA unavailable for request", result.status)
def test_should_raise_unsupported_certificate_request(self):
order_id = '1234'
plugin_meta = {}
self.assertRaises(
NotImplementedError,
self.symantec.check_certificate_status,
order_id,
self.order_meta,
plugin_meta,
self.barbican_plugin_dto
)

View File

@ -222,25 +222,6 @@ class WhenCallingScheduleOrderRetryTasks(database_utils.RepositoryTestCase):
self.assertEqual( self.assertEqual(
'test_should_schedule_invoking_task_for_retry', retry_rpc_method) 'test_should_schedule_invoking_task_for_retry', retry_rpc_method)
def test_should_schedule_certificate_status_task_for_retry(self):
self.result.retry_task = (
common.RetryTasks.INVOKE_CERT_STATUS_CHECK_TASK
)
# Schedule this test method as the passed-in 'retry' function.
retry_rpc_method = server.schedule_order_retry_tasks(
None, # Should be ignored for non-self retries.
self.result,
None, # Not used.
*self.args,
**self.kwargs)
database_utils.get_session().commit() # Flush to the database.
self.assertEqual(
'check_certificate_status', retry_rpc_method)
self._verify_retry_task_entity(
'check_certificate_status')
def _verify_retry_task_entity(self, retry_task): def _verify_retry_task_entity(self, retry_task):
# Retrieve the task retry entity created above and verify it. # Retrieve the task retry entity created above and verify it.
entities, offset, limit, total = self.repo.get_by_create_date() entities, offset, limit, total = self.repo.get_by_create_date()
@ -302,24 +283,6 @@ class WhenCallingTasksMethod(utils.BaseTestCase):
mock.ANY, 'result', None, 'order1234', mock.ANY, 'result', None, 'order1234',
'keystone1234', 'request1234') 'keystone1234', 'request1234')
@mock.patch('barbican.queue.server.schedule_order_retry_tasks')
@mock.patch('barbican.tasks.resources.CheckCertificateStatusOrder')
def test_should_check_certificate_order(
self, mock_check_cert, mock_schedule):
method = mock_check_cert.return_value.process_and_suppress_exceptions
method.return_value = 'result'
self.tasks.check_certificate_status(
None, self.order_id, self.external_project_id, self.request_id)
mock_process = mock_check_cert.return_value
mock_process.process_and_suppress_exceptions.assert_called_with(
self.order_id, self.external_project_id
)
mock_schedule.assert_called_with(
mock.ANY, 'result', None, 'order1234',
'keystone1234', 'request1234')
@mock.patch('barbican.tasks.resources.BeginTypeOrder') @mock.patch('barbican.tasks.resources.BeginTypeOrder')
def test_process_order_catch_exception(self, mock_begin_order): def test_process_order_catch_exception(self, mock_begin_order):
"""Test that BeginTypeOrder's process() handles all exceptions.""" """Test that BeginTypeOrder's process() handles all exceptions."""

File diff suppressed because it is too large Load Diff

View File

@ -254,110 +254,6 @@ class WhenBeginningKeyTypeOrder(BaseOrderTestCase):
self.order_repo.save.assert_called_once_with(self.order) self.order_repo.save.assert_called_once_with(self.order)
class WhenBeginningCertificateTypeOrder(BaseOrderTestCase):
def setUp(self):
super(WhenBeginningCertificateTypeOrder, self).setUp()
self.order.type = models.OrderType.CERTIFICATE
self.resource = resources.BeginTypeOrder()
@mock.patch(
'barbican.tasks.certificate_resources.issue_certificate_request')
def test_should_process_order_no_container(
self, mock_issue_cert_request):
mock_issue_cert_request.return_value = None
result = self.resource.process_and_suppress_exceptions(
self.order.id, self.external_project_id)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
external_project_id=self.external_project_id)
self.assertEqual(self.order.status, models.States.ACTIVE)
mock_issue_cert_request.assert_called_once_with(
self.order,
self.project,
mock.ANY
)
self.assertIsNone(self.order.container_id)
self.assertIsInstance(result, common.FollowOnProcessingStatusDTO)
@mock.patch(
'barbican.tasks.certificate_resources.issue_certificate_request')
def test_should_process_order_with_container(
self, mock_issue_cert_request):
mock_issue_cert_request.return_value = self.container
result = self.resource.process(
self.order.id, self.external_project_id)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
external_project_id=self.external_project_id)
self.assertEqual(self.order.status, models.States.ACTIVE)
mock_issue_cert_request.assert_called_once_with(
self.order,
self.project,
mock.ANY
)
self.assertEqual(self.container.id, self.order.container_id)
self.assertIsInstance(result, common.FollowOnProcessingStatusDTO)
class WhenUpdatingOrder(BaseOrderTestCase):
def setUp(self):
super(WhenUpdatingOrder, self).setUp()
self.updated_meta = 'updated'
self.resource = resources.UpdateOrder()
@mock.patch(
'barbican.tasks.certificate_resources.modify_certificate_request')
def test_should_update_certificate_order(self, mock_modify_cert_request):
self.order.type = models.OrderType.CERTIFICATE
self.resource.process_and_suppress_exceptions(
self.order.id, self.external_project_id, self.updated_meta)
self.assertEqual(self.order.status, models.States.ACTIVE)
mock_modify_cert_request.assert_called_once_with(
self.order,
self.updated_meta
)
@mock.patch(
'barbican.tasks.certificate_resources.modify_certificate_request')
def test_should_fail_during_processing(self, mock_mod_cert):
mock_mod_cert.side_effect = ValueError('Abort!')
self.order.type = models.OrderType.CERTIFICATE
exception = self.assertRaises(
ValueError,
self.resource.process,
self.order_id,
self.external_project_id,
self.meta
)
self.assertEqual('Abort!', str(exception))
mock_mod_cert.assert_called_once_with(self.order, self.meta)
self.assertEqual(models.States.ERROR, self.order.status)
self.assertEqual(500, self.order.error_status_code)
self.assertEqual(u._('Update Order failure seen - please contact '
'site administrator.'), self.order.error_reason)
class WhenBeginningAsymmetricTypeOrder(BaseOrderTestCase): class WhenBeginningAsymmetricTypeOrder(BaseOrderTestCase):
def setUp(self): def setUp(self):
@ -457,75 +353,3 @@ class WhenBeginningAsymmetricTypeOrder(BaseOrderTestCase):
self.project_repo.get.assert_called_once_with(self.project_id) self.project_repo.get.assert_called_once_with(self.project_id)
self.order_repo.save.assert_called_once_with(self.order) self.order_repo.save.assert_called_once_with(self.order)
class WhenCheckingCertificateStatus(BaseOrderTestCase):
def setUp(self):
super(WhenCheckingCertificateStatus, self).setUp()
self.order.type = models.OrderType.CERTIFICATE
self.resource = resources.CheckCertificateStatusOrder()
@mock.patch(
'barbican.tasks.certificate_resources.check_certificate_request')
def test_should_process_order_no_container(
self, mock_check_cert_request):
mock_check_cert_request.return_value = None
result = self.resource.process_and_suppress_exceptions(
self.order.id, self.external_project_id)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
external_project_id=self.external_project_id)
self.assertEqual(self.order.status, models.States.ACTIVE)
mock_check_cert_request.assert_called_once_with(
self.order,
self.project,
mock.ANY
)
self.assertIsNone(self.order.container_id)
self.assertIsInstance(result, common.FollowOnProcessingStatusDTO)
@mock.patch(
'barbican.tasks.certificate_resources.check_certificate_request')
def test_should_process_order_with_container(
self, mock_check_cert_request):
mock_check_cert_request.return_value = self.container
self.resource.process(self.order.id, self.external_project_id)
self.order_repo.get.assert_called_once_with(
entity_id=self.order.id,
external_project_id=self.external_project_id)
self.assertEqual(self.order.status, models.States.ACTIVE)
mock_check_cert_request.assert_called_once_with(
self.order,
self.project,
mock.ANY
)
self.assertEqual(self.container.id, self.order.container_id)
def test_should_fail_with_bogus_order_type(self):
self.order.type = 'bogus-type'
self.assertRaises(
NotImplementedError,
self.resource.process,
self.order.id,
self.external_project_id,
)
# Order state should be set to ERROR.
self.assertEqual(models.States.ERROR, self.order.status)
self.assertEqual(
('Check Certificate Order Status failure seen - '
'please contact site administrator.'),
self.order.error_reason)
self.assertEqual(500, self.order.error_status_code)

View File

@ -113,11 +113,7 @@ function configure_dogtag_plugin {
iniset $BARBICAN_CONF dogtag_plugin nss_db_path '/etc/barbican/alias' iniset $BARBICAN_CONF dogtag_plugin nss_db_path '/etc/barbican/alias'
iniset $BARBICAN_CONF dogtag_plugin nss_db_path_ca '/etc/barbican/alias-ca' iniset $BARBICAN_CONF dogtag_plugin nss_db_path_ca '/etc/barbican/alias-ca'
iniset $BARBICAN_CONF dogtag_plugin nss_password 'password123' iniset $BARBICAN_CONF dogtag_plugin nss_password 'password123'
iniset $BARBICAN_CONF dogtag_plugin simple_cmc_profile 'caOtherCert'
iniset $BARBICAN_CONF dogtag_plugin ca_expiration_time 1
iniset $BARBICAN_CONF dogtag_plugin plugin_working_dir '/etc/barbican/dogtag'
iniset $BARBICAN_CONF secretstore enabled_secretstore_plugins dogtag_crypto iniset $BARBICAN_CONF secretstore enabled_secretstore_plugins dogtag_crypto
iniset $BARBICAN_CONF certificate enabled_certificate_plugins dogtag
} }
# configure_barbican - Set config files, create data dirs, etc # configure_barbican - Set config files, create data dirs, etc

View File

@ -1,7 +1,5 @@
[DEFAULT] [DEFAULT]
output_file = etc/barbican/barbican.conf.sample output_file = etc/barbican/barbican.conf.sample
namespace = barbican.certificate.plugin
namespace = barbican.certificate.plugin.snakeoil
namespace = barbican.common.config namespace = barbican.common.config
namespace = barbican.plugin.crypto namespace = barbican.plugin.crypto
namespace = barbican.plugin.crypto.p11 namespace = barbican.plugin.crypto.p11

View File

@ -15,8 +15,6 @@
import testtools import testtools
from barbican.plugin.interface import certificate_manager as cert_interface
from functionaltests.api import base from functionaltests.api import base
from functionaltests.api.v1.behaviors import consumer_behaviors from functionaltests.api.v1.behaviors import consumer_behaviors
from functionaltests.api.v1.behaviors import container_behaviors from functionaltests.api.v1.behaviors import container_behaviors
@ -36,11 +34,6 @@ admin_b = CONF.rbac_users.admin_b
service_admin = CONF.identity.service_admin service_admin = CONF.identity.service_admin
def is_ca_backend_snakeoil():
return 'snakeoil_ca' in\
cert_interface.CONF.certificate.enabled_certificate_plugins
@testtools.testcase.attr('no_parallel') @testtools.testcase.attr('no_parallel')
class QuotaEnforcementTestCase(base.TestCase): class QuotaEnforcementTestCase(base.TestCase):

View File

@ -1,7 +0,0 @@
---
deprecations:
- |
The Symantec certificate plugin has been deprecated, because lack of
maintenance activity of the plugin. Also the symantecssl python library
is not generally available and the plugin has never been tested in
upstream.

View File

@ -0,0 +1,5 @@
---
upgrade:
- |
The certificate plugin and the certificate event plugin were both removed,
because these were used for deprecated certificate resources.

View File

@ -62,13 +62,6 @@ barbican.secretstore.plugin =
barbican.crypto.plugin = barbican.crypto.plugin =
p11_crypto = barbican.plugin.crypto.p11_crypto:P11CryptoPlugin p11_crypto = barbican.plugin.crypto.p11_crypto:P11CryptoPlugin
simple_crypto = barbican.plugin.crypto.simple_crypto:SimpleCryptoPlugin simple_crypto = barbican.plugin.crypto.simple_crypto:SimpleCryptoPlugin
barbican.certificate.plugin =
simple_certificate = barbican.plugin.simple_certificate_manager:SimpleCertificatePlugin
snakeoil_ca = barbican.plugin.snakeoil_ca:SnakeoilCACertificatePlugin
symantec = barbican.plugin.symantec:SymantecCertificatePlugin
dogtag = barbican.plugin.dogtag:DogtagCAPlugin
barbican.certificate.event.plugin =
simple_certificate_event = barbican.plugin.simple_certificate_manager:SimpleCertificateEventPlugin
barbican.test.crypto.plugin = barbican.test.crypto.plugin =
test_crypto = barbican.tests.crypto.test_plugin:TestCryptoPlugin test_crypto = barbican.tests.crypto.test_plugin:TestCryptoPlugin
oslo.config.opts = oslo.config.opts =
@ -80,7 +73,5 @@ oslo.config.opts =
barbican.plugin.crypto.p11 = barbican.plugin.crypto.p11_crypto:list_opts barbican.plugin.crypto.p11 = barbican.plugin.crypto.p11_crypto:list_opts
barbican.plugin.secret_store.kmip = barbican.plugin.kmip_secret_store:list_opts barbican.plugin.secret_store.kmip = barbican.plugin.kmip_secret_store:list_opts
barbican.plugin.secret_store.vault = barbican.plugin.vault_secret_store:list_opts barbican.plugin.secret_store.vault = barbican.plugin.vault_secret_store:list_opts
barbican.certificate.plugin = barbican.plugin.interface.certificate_manager:list_opts
barbican.certificate.plugin.snakeoil = barbican.plugin.snakeoil_ca:list_opts
oslo.config.opts.defaults = oslo.config.opts.defaults =
barbican.common.config = barbican.common.config:set_lib_defaults barbican.common.config = barbican.common.config:set_lib_defaults