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. """ successful, error_msg, can_retry, certificate, intermediate, root = self._ca_get_order_status(order_id) status = cert.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST message = None if successful: if error_msg == "CANCELLED": status = cert.CertificateStatus.REQUEST_CANCELED elif error_msg == "PENDING_REISSUE": status = cert.CertificateStatus.WAITING_FOR_CA else: status = cert.CertificateStatus.CERTIFICATE_GENERATED else: status = cert.CertificateStatus.WAITING_FOR_CA message = error_msg return cert.ResultDTO(status=status, status_message=message) return cert.ResultDTO(status=status, status_message=message, certificate=certificate, intermediates=({"type":"INTERMEDIATE", "cert":intermediate},{"type":"ROOT", "cert":root}))
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 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 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"))
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 = self._ca_create_order(order_id, order_meta, plugin_meta) status = cert.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST message = error_msg 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 _issue_custom_certificate_request(self, order_id, order_meta, plugin_meta): """Issue a custom certificate request to Dogtag CA For now, we assume that we are talking to the Dogtag CA that is deployed with the KRA back-end, and we are connected as a CA agent. This means that we can use the agent convenience method to automatically approve the certificate request. :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. :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")) results = self.certclient.enroll_cert(profile_id, order_meta) return self._process_enrollment_results(results, plugin_meta)
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
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 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. """ """order_meta should cover: - CSR - ApproverEmail - order_id - the order ID that was used for the certificate """ successful, error_msg, can_retry = self._ca_reissue_cert(order_id, order_meta, plugin_meta) if successful: status = cert.CertificateStatus.WAITING_FOR_CA elif can_retry: status = cert.CertificateStatus.CLIENT_DATA_ISSUE_SEEN else: status = cert.CertificateStatus.CLIENT_DATA_ISSUE_SEEN message = error_msg return cert.ResultDTO(status=status, status_message=message)
def _process_enrollment_results(self, enrollment_results, plugin_meta): """Process results received from Dogtag CA for enrollment :param enrollment_results: list of CertEnrollmentResult objects :param plugin_meta: metadata dict for storing plugin specific data :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 if not cert: 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: raise cm.CertificateGeneralException( u._("request_id {req_id} returns COMPLETE but no cert " "returned").format(req_id=request.request_id)) else: raise cm.CertificateGeneralException( u._("Invalid request_status {status} for " "request_id {request_id}").format( status=request_status, request_id=request.request_id)) return cm.ResultDTO(cm.CertificateStatus.CERTIFICATE_GENERATED, certificate=cert.encoded, intermediates=cert.pkcs7_cert_chain)
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))
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 = 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) cert = self.cert_manager.make_certificate(csr) cert_enc = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) ca_enc = crypto.dump_certificate(crypto.FILETYPE_PEM, self.ca.cert) return cert_manager.ResultDTO( cert_manager.CertificateStatus.CERTIFICATE_GENERATED, certificate=base64.b64encode(cert_enc), intermediates=base64.b64encode(ca_enc))
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 check_certificate_status(self, order_id, order_meta, plugin_meta): """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. :returns: A :class:`ResultDTO` instance containing the result populated by the plugin implementation :rtype: :class:`ResultDTO` """ LOG.info(u._LI('Invoking check_certificate_status()')) 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(u._LI('Invoking cancel_certificate_request()')) return cert.ResultDTO(cert.CertificateStatus.REQUEST_CANCELED)
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 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. """ successful, error_msg, can_retry = self._ca_modify_order(order_id, "CANCEL") if successful: status = cert.CertificateStatus.REQUEST_CANCELED else: status = cert.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST return cert.ResultDTO(status=status, status_message=error_msg)
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)
def setUp(self): super(BaseCertificateRequestsTestCase, self).setUp() self.external_project_id = "56789" self.project = res.get_or_create_project(self.external_project_id) project_repo.save(self.project) self.barbican_meta_dto = mock.MagicMock() self.order_meta = {} self.plugin_meta = {} self.barbican_meta = {} self.result = cert_man.ResultDTO( cert_man.CertificateStatus.WAITING_FOR_CA ) self.result_follow_on = common.FollowOnProcessingStatusDTO() self.cert_plugin = mock.MagicMock() self.cert_plugin.issue_certificate_request.return_value = self.result self.cert_plugin.check_certificate_status.return_value = self.result self.store_plugin = mock.MagicMock() parsed_ca = { 'plugin_name': "cert_plugin", 'plugin_ca_id': "XXXX", 'name': "test ca", 'description': 'Test CA', 'ca_signing_certificate': 'ZZZZZ', 'intermediates': 'YYYYY' } self.ca = models.CertificateAuthority(parsed_ca) ca_repo.create_from(self.ca) self.ca_id = self.ca.id # second ca for testing parsed_ca = { 'plugin_name': "cert_plugin", 'plugin_ca_id': "XXXX2", 'name': "test ca2", 'description': 'Test CA2', 'ca_signing_certificate': 'ZZZZZ2', 'intermediates': 'YYYYY2' } self.ca2 = models.CertificateAuthority(parsed_ca) ca_repo.create_from(self.ca2) self.ca_id2 = self.ca2.id # data for preferred CA and global preferred CA tests # add those to the repo in those tests self.pref_ca = models.PreferredCertificateAuthority( self.project.id, self.ca_id) self.global_pref_ca = models.PreferredCertificateAuthority( self.project.id, self.ca_id) # data for stored key cases self.private_key = models.Secret() self.private_key.secret_type = 'PRIVATE' self.private_key.project_id = self.project.id secret_repo.create_from(self.private_key) self.public_key = models.Secret() self.public_key.secret_type = 'PUBLIC' self.public_key.project_id = self.project.id secret_repo.create_from(self.public_key) self.passphrase = models.Secret() self.passphrase.secret_type = 'PASSPHRASE' self.passphrase.project_id = self.project.id secret_repo.create_from(self.passphrase) self.private_key_value = None self.public_key_value = "public_key" self.passphrase_value = None self.parsed_container_with_passphrase = { 'name': 'container name', 'type': 'rsa', 'secret_refs': [ {'name': 'private_key', 'secret_ref': 'https://localhost/secrets/' + self.private_key.id}, {'name': 'public_key', 'secret_ref': 'https://localhost/secrets/' + self.public_key.id}, {'name': 'private_key_passphrase', 'secret_ref': 'https://localhost/secrets/' + self.passphrase.id} ] } self.parsed_container = { 'name': 'container name', 'type': 'rsa', 'secret_refs': [ {'name': 'private_key', 'secret_ref': 'https://localhost/secrets/' + self.private_key.id}, {'name': 'public_key', 'secret_ref': 'https://localhost/secrets/' + self.public_key.id} ] } self.container_with_passphrase = models.Container( self.parsed_container_with_passphrase) self.container_with_passphrase.project_id = self.project.id container_repo.create_from(self.container_with_passphrase) self.container = models.Container(self.parsed_container) self.container.project_id = self.project.id container_repo.create_from(self.container) repositories.commit() self.stored_key_meta = { cert_man.REQUEST_TYPE: cert_man.CertificateRequestType.STORED_KEY_REQUEST, "container_ref": "https://localhost/containers/" + self.container.id, "subject_dn": "cn=host.example.com,ou=dev,ou=us,o=example.com" } self.order = models.Order() self.order.meta = self.order_meta self.order.project_id = self.project.id self.order.order_barbican_meta = self.barbican_meta self.order.type = 'certificate' order_repo.create_from(self.order) self._config_cert_plugin() self._config_store_plugin() self._config_cert_event_plugin() self._config_save_meta_plugin() self._config_get_meta_plugin() self._config_save_barbican_meta_plugin() self._config_get_barbican_meta_plugin() self._config_barbican_meta_dto()