From 6a783124bfd3eea8cd180fb727470f411f3ab735 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Mon, 21 May 2018 16:32:28 +0100 Subject: [PATCH] ENT-1821 - Doorman failed with parallel requests (#827) * attempt to fix race condition by introducing thread lock --- network-management/build.gradle | 2 +- .../networkmanage/doorman/CrrJiraCient.kt | 23 +--- .../networkmanage/doorman/CsrJiraCient.kt | 55 +++------ .../corda/networkmanage/doorman/JiraCient.kt | 111 ++++++++++++------ .../doorman/signer/JiraCrrHandler.kt | 7 +- .../doorman/signer/JiraCsrHandler.kt | 15 +-- .../doorman/CsrJiraClientTest.kt | 5 +- .../doorman/signer/JiraCsrHandlerTest.kt | 14 +-- 8 files changed, 119 insertions(+), 113 deletions(-) diff --git a/network-management/build.gradle b/network-management/build.gradle index 09ccb8c51e..771a485029 100644 --- a/network-management/build.gradle +++ b/network-management/build.gradle @@ -98,7 +98,7 @@ dependencies { // Unit testing helpers. testCompile "junit:junit:$junit_version" testCompile "org.assertj:assertj-core:${assertj_version}" - testCompile "com.nhaarman:mockito-kotlin:0.6.1" + testCompile "com.nhaarman:mockito-kotlin:1.5.0" testCompile "com.spotify:docker-client:8.9.1" compile('com.atlassian.jira:jira-rest-java-client-core:5.0.4') { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CrrJiraCient.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CrrJiraCient.kt index 878696f4ff..9430020f72 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CrrJiraCient.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CrrJiraCient.kt @@ -12,22 +12,11 @@ package com.r3.corda.networkmanage.doorman import com.atlassian.jira.rest.client.api.JiraRestClient import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder -import com.atlassian.jira.rest.client.api.domain.input.TransitionInput import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData import net.corda.core.identity.CordaX500Name -import net.corda.core.utilities.contextLogger class CrrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClient(restClient, projectCode) { - companion object { - val logger = contextLogger() - } - fun createCertificateRevocationRequestTicket(revocationRequest: CertificateRevocationRequestData) { - // Check there isn't already a ticket for this request. - if (getIssueById(revocationRequest.requestId) != null) { - logger.warn("There is already a ticket corresponding to request Id ${revocationRequest.requestId}, not creating a new one.") - return - } val ticketDescription = "Legal name: ${revocationRequest.legalName}\n" + "Certificate serial number: ${revocationRequest.certificateSerialNumber}\n" + "Revocation reason: ${revocationRequest.reason.name}\n" + @@ -46,16 +35,8 @@ class CrrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClien .setDescription(ticketDescription) .setSummary(ticketSummary) .setFieldValue(requestIdField.id, revocationRequest.requestId) - // This will block until the issue is created. - restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim().key - val createdIssue = checkNotNull(getIssueById(revocationRequest.requestId)) { "Missing the JIRA ticket for the request ID: ${revocationRequest.requestId}" } - restClient.issueClient.addAttachment(createdIssue.attachmentsUri, revocationRequest.certificate.encoded.inputStream(), "${revocationRequest.certificateSerialNumber}.cer") - .fail { logger.error("Error processing request '${createdIssue.key}' : Exception when uploading attachment to JIRA.", it) }.claim() - } - fun updateDoneCertificateRevocationRequest(requestId: String) { - logger.debug("Marking JIRA ticket with `request ID` = $requestId as DONE.") - val issue = requireNotNull(getIssueById(requestId)) { "Missing the JIRA ticket for the request ID: $requestId" } - restClient.issueClient.transition(issue, TransitionInput(getTransitionId(DONE_TRANSITION_KEY, issue))).fail { logger.error("Exception when transiting JIRA status.", it) }.claim() + val attachment = Pair("${revocationRequest.certificateSerialNumber}.cer", revocationRequest.certificate.encoded.inputStream()) + createJiraTicket(revocationRequest.requestId, issue.build(), attachment) } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraCient.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraCient.kt index 96850e9fc6..0737292ee5 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraCient.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraCient.kt @@ -12,44 +12,22 @@ package com.r3.corda.networkmanage.doorman import com.atlassian.jira.rest.client.api.JiraRestClient import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder -import com.atlassian.jira.rest.client.api.domain.input.TransitionInput import com.r3.corda.networkmanage.common.utils.getCertRole import com.r3.corda.networkmanage.common.utils.getEmail import net.corda.core.identity.CordaX500Name -import net.corda.core.utilities.contextLogger -import net.corda.nodeapi.internal.crypto.X509Utilities import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter -import java.security.cert.CertPath import javax.security.auth.x500.X500Principal class CsrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClient(restClient, projectCode) { - companion object { - val logger = contextLogger() - } - - // TODO: Pass in a parsed object instead of raw PKCS10 request. - fun createCertificateSigningRequestTicket(requestId: String, signingRequest: PKCS10CertificationRequest) { - // Check there isn't already a ticket for this request. - if (getIssueById(requestId) != null) { - logger.warn("There is already a ticket corresponding to request Id $requestId, not creating a new one.") - return - } - - // Make sure request has been accepted. - val request = StringWriter() - JcaPEMWriter(request).use { - it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded)) - } + fun createCertificateSigningRequestTicket(requestData: CertificationRequestData) { + val (requestId, signingRequest) = requestData // TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't // have to do it again here. val subject = CordaX500Name.build(X500Principal(signingRequest.subject.encoded)) - val email = signingRequest.getEmail() - - val certRole = signingRequest.getCertRole() val ticketSummary = if (subject.organisationUnit != null) { "${subject.organisationUnit}, ${subject.organisation}" @@ -57,33 +35,36 @@ class CsrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClien subject.organisation } - val data = mapOf("Requested Role Type" to certRole.name, + val data = mapOf( + "Requested Role Type" to signingRequest.getCertRole().name, "Common Name" to subject.commonName, "Organisation" to subject.organisation, "Organisation Unit" to subject.organisationUnit, "State" to subject.state, "Nearest City" to subject.locality, "Country" to subject.country, - "Email" to email, + "Email" to signingRequest.getEmail(), "X500 Name" to subject.toString()) - val ticketDescription = data.filter { it.value != null }.map { "${it.key}: ${it.value}" }.joinToString("\n") + "\n\n{code}$request{code}" + val requestPemString = StringWriter().apply { + JcaPEMWriter(this).use { + it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded)) + } + }.toString() + + val ticketDescription = data.filter { it.value != null }.map { "${it.key}: ${it.value}" }.joinToString("\n") + "\n\n{code}$requestPemString{code}" val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id) .setProjectKey(projectCode) .setDescription(ticketDescription) .setSummary(ticketSummary) .setFieldValue(requestIdField.id, requestId) - // This will block until the issue is created. - restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim() - } - fun updateDoneCertificateSigningRequest(requestId: String, certPath: CertPath) { - // Retrieving certificates for signed CSRs to attach to the jira tasks. - val certificate = certPath.certificates.first() - val issue = requireNotNull(getIssueById(requestId)) { "Cannot find the JIRA ticket `request ID` = $requestId" } - restClient.issueClient.transition(issue, TransitionInput(getTransitionId(DONE_TRANSITION_KEY, issue))).fail { logger.error("Exception when transiting JIRA status.", it) }.claim() - restClient.issueClient.addAttachment(issue.attachmentsUri, certificate.encoded.inputStream(), "${X509Utilities.CORDA_CLIENT_CA}.cer") - .fail { logger.error("Error processing request '${issue.key}' : Exception when uploading attachment to JIRA.", it) }.claim() + createJiraTicket(requestId, issue.build()) } } + +// TODO: Parse PKCS10 request. +data class CertificationRequestData(val requestId: String, val rawRequest: PKCS10CertificationRequest) + + diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt index a39d796114..dde9586b5b 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt @@ -16,70 +16,109 @@ import com.atlassian.jira.rest.client.api.domain.Comment import com.atlassian.jira.rest.client.api.domain.Field import com.atlassian.jira.rest.client.api.domain.Issue import com.atlassian.jira.rest.client.api.domain.IssueType +import com.atlassian.jira.rest.client.api.domain.input.IssueInput import com.atlassian.jira.rest.client.api.domain.input.TransitionInput +import com.r3.corda.networkmanage.doorman.JiraConstant.DONE_TRANSITION_KEY +import com.r3.corda.networkmanage.doorman.JiraConstant.START_TRANSITION_KEY +import com.r3.corda.networkmanage.doorman.JiraConstant.STOP_TRANSITION_KEY +import net.corda.core.internal.ConcurrentBox import net.corda.core.utilities.contextLogger import org.slf4j.Logger +import java.io.InputStream -abstract class JiraClient(protected val restClient: JiraRestClient, protected val projectCode: String) { +/** + * The Jira client class manages concurrent access to the [JiraRestClient] to ensure atomic read and write for some operations to prevent race condition. + */ +abstract class JiraClient(restClient: JiraRestClient, protected val projectCode: String) { companion object { val logger = contextLogger() - - const val DONE_TRANSITION_KEY = "Done" - const val START_TRANSITION_KEY = "Start Progress" - const val STOP_TRANSITION_KEY = "Stop Progress" - } + private val restClientLock = ConcurrentBox(restClient) + // The JIRA project must have a Request ID and reject reason field, and the Task issue type. protected val requestIdField: Field = requireNotNull(restClient.metadataClient.fields.claim().find { it.name == "Request ID" }) { "Request ID field not found in JIRA '$projectCode'" } protected val taskIssueType: IssueType = requireNotNull(restClient.metadataClient.issueTypes.claim().find { it.name == "Task" }) { "Task issue type field not found in JIRA '$projectCode'" } - protected val rejectReasonField: Field = requireNotNull(restClient.metadataClient.fields.claim().find { it.name == "Reject Reason" }) { "Reject Reason field not found in JIRA '$projectCode'" } + private val rejectReasonField: Field = requireNotNull(restClient.metadataClient.fields.claim().find { it.name == "Reject Reason" }) { "Reject Reason field not found in JIRA '$projectCode'" } private val transitions = mutableMapOf() fun getApprovedRequests(): List { - val issues = restClient.searchClient.searchJql("project = $projectCode AND status = Approved").claim().issues - return issues.mapNotNull { issue -> - val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." } - // Issue retrieved via search doesn't contain change logs. - val fullIssue = restClient.issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim() - val approvedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Approved" } } - ApprovedRequest(requestId, approvedBy?.author?.displayName ?: "Unknown") + return restClientLock.concurrent { + val issues = searchClient.searchJql("project = $projectCode AND status = Approved").claim().issues + issues.mapNotNull { issue -> + val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." } + // Issue retrieved via search doesn't contain change logs. + val fullIssue = issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim() + val approvedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Approved" } } + ApprovedRequest(requestId, approvedBy?.author?.displayName ?: "Unknown") + } } } fun getRejectedRequests(): List { - val issues = restClient.searchClient.searchJql("project = $projectCode AND status = Rejected").claim().issues - return issues.mapNotNull { issue -> - val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." } - val rejectedReason = issue.getField(rejectReasonField.id)?.value?.toString() - // Issue retrieved via search doesn't contain comments. - val fullIssue = restClient.issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim() - val rejectedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Rejected" } } - RejectedRequest(requestId, rejectedBy?.author?.displayName ?: "Unknown", rejectedReason) + return restClientLock.concurrent { + val issues = searchClient.searchJql("project = $projectCode AND status = Rejected").claim().issues + issues.mapNotNull { issue -> + val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." } + val rejectedReason = issue.getField(rejectReasonField.id)?.value?.toString() + // Issue retrieved via search doesn't contain comments. + val fullIssue = issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim() + val rejectedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Rejected" } } + RejectedRequest(requestId, rejectedBy?.author?.displayName ?: "Unknown", rejectedReason) + } } } fun updateRejectedRequest(requestId: String) { - val issue = requireNotNull(getIssueById(requestId)) { "Issue with the `request ID` = $requestId does not exist." } - // Move status to in progress. - restClient.issueClient.transition(issue, TransitionInput(getTransitionId(START_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim() - // Move status to stopped. - restClient.issueClient.transition(issue, TransitionInput(getTransitionId(STOP_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim() - restClient.issueClient.addComment(issue.commentsUri, Comment.valueOf("Request cancelled by doorman.")).claim() - + restClientLock.exclusive { + val issue = requireNotNull(getIssueById(requestId)) { "Issue with the `request ID` = $requestId does not exist." } + // Move status to in progress. + issueClient.transition(issue, TransitionInput(getTransitionId(START_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim() + // Move status to stopped. + issueClient.transition(issue, TransitionInput(getTransitionId(STOP_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim() + issueClient.addComment(issue.commentsUri, Comment.valueOf("Request cancelled by doorman.")).claim() + } } - protected fun getIssueById(requestId: String): Issue? { + private fun JiraRestClient.getIssueById(requestId: String): Issue? { // Jira only support ~ (contains) search for custom textfield. - return restClient.searchClient.searchJql("'Request ID' ~ $requestId").claim().issues.firstOrNull() + return searchClient.searchJql("'Request ID' ~ $requestId").claim().issues.firstOrNull() } - protected fun getTransitionId(transitionKey: String, issue: Issue): Int { + private fun getTransitionId(transitionKey: String, issue: Issue): Int { return transitions.computeIfAbsent(transitionKey, { key -> - restClient.issueClient.getTransitions(issue.transitionsUri).claim().single { it.name == key }.id + restClientLock.concurrent { + issueClient.getTransitions(issue.transitionsUri).claim().single { it.name == key }.id + } }) } + + protected fun createJiraTicket(requestId: String, issue: IssueInput, attachment: Pair? = null) { + restClientLock.exclusive { + if (getIssueById(requestId) != null) { + logger.warn("There is already a ticket corresponding to request Id $requestId, not creating a new one.") + return + } + // This will block until the issue is created. + issueClient.createIssue(issue).fail { logger.error("Exception when creating JIRA issue for request: '$requestId'.", it) }.claim() + attachment?.let { addAttachment(requestId, it) } + } + } + + fun transitRequestStatusToDone(requestId: String, attachment: Pair? = null) { + restClientLock.exclusive { + val issue = requireNotNull(getIssueById(requestId)) { "Cannot find the JIRA ticket `request ID` = $requestId" } + issueClient.transition(issue, TransitionInput(getTransitionId(DONE_TRANSITION_KEY, issue))).fail { logger.error("Exception when transiting JIRA status.", it) }.claim() + attachment?.let { addAttachment(requestId, it) } + } + } + + private fun JiraRestClient.addAttachment(requestId: String, attachment: Pair) { + val createdIssue = checkNotNull(getIssueById(requestId)) { "Missing the JIRA ticket for the request ID: $requestId" } + issueClient.addAttachment(createdIssue.attachmentsUri, attachment.second, attachment.first) + .fail { logger.error("Error processing request '${createdIssue.key}' : Exception when uploading attachment to JIRA.", it) }.claim() + } } data class ApprovedRequest(val requestId: String, val approvedBy: String) @@ -95,3 +134,9 @@ inline fun Iterable.forEachWithExceptionLogging(logger: Logger, act } } } + +object JiraConstant{ + const val DONE_TRANSITION_KEY = "Done" + const val START_TRANSITION_KEY = "Start Progress" + const val STOP_TRANSITION_KEY = "Stop Progress" +} diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCrrHandler.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCrrHandler.kt index 57a72a5cc3..24e885c838 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCrrHandler.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCrrHandler.kt @@ -13,10 +13,7 @@ package com.r3.corda.networkmanage.doorman.signer import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestStorage import com.r3.corda.networkmanage.common.persistence.RequestStatus -import com.r3.corda.networkmanage.doorman.ApprovedRequest -import com.r3.corda.networkmanage.doorman.CrrJiraClient -import com.r3.corda.networkmanage.doorman.RejectedRequest -import com.r3.corda.networkmanage.doorman.forEachWithExceptionLogging +import com.r3.corda.networkmanage.doorman.* import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.network.CertificateRevocationRequest @@ -62,7 +59,7 @@ class JiraCrrHandler(private val jiraClient: CrrJiraClient, logger.debug("Updating JIRA tickets: `approved` = $approvedRequest, `rejected` = $rejectedRequest") approvedRequest.mapNotNull { crrStorage.getRevocationRequest(it.requestId) } .filter { it.status == RequestStatus.DONE } - .forEachWithExceptionLogging(logger) { jiraClient.updateDoneCertificateRevocationRequest(it.requestId) } + .forEachWithExceptionLogging(logger) { jiraClient.transitRequestStatusToDone(it.requestId) } rejectedRequest.mapNotNull { crrStorage.getRevocationRequest(it.requestId) } .filter { it.status == RequestStatus.REJECTED } .forEachWithExceptionLogging(logger) { jiraClient.updateRejectedRequest(it.requestId) } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandler.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandler.kt index 2dd1d40e29..ebd1dd6579 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandler.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandler.kt @@ -14,11 +14,9 @@ import com.r3.corda.networkmanage.common.persistence.CertificateResponse import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequestStorage import com.r3.corda.networkmanage.common.persistence.RequestStatus -import com.r3.corda.networkmanage.doorman.ApprovedRequest -import com.r3.corda.networkmanage.doorman.CsrJiraClient -import com.r3.corda.networkmanage.doorman.RejectedRequest -import com.r3.corda.networkmanage.doorman.forEachWithExceptionLogging +import com.r3.corda.networkmanage.doorman.* import net.corda.core.utilities.contextLogger +import net.corda.nodeapi.internal.crypto.X509Utilities import org.bouncycastle.pkcs.PKCS10CertificationRequest class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage: CertificateSigningRequestStorage, private val delegate: CsrHandler) : CsrHandler by delegate { @@ -31,7 +29,7 @@ class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage: // Make sure request has been accepted. try { if (delegate.getResponse(requestId) !is CertificateResponse.Unauthorised) { - jiraClient.createCertificateSigningRequestTicket(requestId, rawRequest) + jiraClient.createCertificateSigningRequestTicket(CertificationRequestData(requestId, rawRequest)) storage.markRequestTicketCreated(requestId) } } catch (e: Exception) { @@ -66,7 +64,10 @@ class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage: approvedRequest.mapNotNull { storage.getRequest(it.requestId) } .filter { it.status == RequestStatus.DONE && it.certData != null } .forEachWithExceptionLogging(logger) { - jiraClient.updateDoneCertificateSigningRequest(it.requestId, it.certData!!.certPath) + val attachment = it.certData?.certPath?.certificates?.firstOrNull()?.let { + Pair("${X509Utilities.CORDA_CLIENT_CA}.cer", it.encoded.inputStream()) + } + jiraClient.transitRequestStatusToDone(it.requestId, attachment) } rejectedRequest.mapNotNull { storage.getRequest(it.requestId) } .filter { it.status == RequestStatus.REJECTED } @@ -88,7 +89,7 @@ class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage: } private fun createTicket(signingRequest: CertificateSigningRequest) { - jiraClient.createCertificateSigningRequestTicket(signingRequest.requestId, signingRequest.request) + jiraClient.createCertificateSigningRequestTicket(CertificationRequestData(signingRequest.requestId, signingRequest.request)) storage.markRequestTicketCreated(signingRequest.requestId) } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraClientTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraClientTest.kt index 3ca4198f2e..185c5f0b31 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraClientTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/CsrJiraClientTest.kt @@ -34,7 +34,7 @@ class CsrJiraClientTest { @Test fun createRequestTicket() { val request = X509Utilities.createCertificateSigningRequest(CordaX500Name("JiraAPITest", "R3 Ltd 3", "London", "GB").x500Principal, "test@test.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - jiraClient.createCertificateSigningRequestTicket(SecureHash.randomSHA256().toString(), request) + jiraClient.createCertificateSigningRequestTicket(CertificationRequestData(SecureHash.randomSHA256().toString(), request)) } @Test @@ -54,7 +54,8 @@ class CsrJiraClientTest { X500Principal("O=test,L=london,C=GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))) jiraClient.getApprovedRequests().forEach { - jiraClient.updateDoneCertificateSigningRequest(it.requestId, selfSignedCaCertPath) + val attachment = Pair("${X509Utilities.CORDA_CLIENT_CA}.cer", selfSignedCaCertPath.certificates.first().encoded.inputStream()) + jiraClient.transitRequestStatusToDone(it.requestId, attachment) } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandlerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandlerTest.kt index 440a32d14d..4f34badf63 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandlerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/JiraCsrHandlerTest.kt @@ -14,6 +14,7 @@ import com.nhaarman.mockito_kotlin.* import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.* import com.r3.corda.networkmanage.doorman.ApprovedRequest +import com.r3.corda.networkmanage.doorman.CertificationRequestData import com.r3.corda.networkmanage.doorman.CsrJiraClient import com.r3.corda.networkmanage.doorman.RejectedRequest import net.corda.core.crypto.Crypto @@ -66,7 +67,7 @@ class JiraCsrHandlerTest : TestBase() { fun `If jira connection fails we don't mark the ticket as created`() { whenever(defaultCsrHandler.saveRequest(any())).thenReturn(requestId) whenever(defaultCsrHandler.getResponse(requestId)).thenReturn(certificateResponse) - whenever(jiraClient.createCertificateSigningRequestTicket(eq(requestId), any())).thenThrow(IllegalStateException("something broke")) + whenever(jiraClient.createCertificateSigningRequestTicket(any())).thenThrow(IllegalStateException("something broke")) // Test jiraCsrHandler.saveRequest(pkcS10CertificationRequest) @@ -93,11 +94,10 @@ class JiraCsrHandlerTest : TestBase() { status = RequestStatus.NEW, request = pkcS10CertificationRequest) whenever(certificationRequestStorage.getRequests(RequestStatus.NEW)).thenReturn(listOf(csr)) - // Test jiraCsrHandler.processRequests() - verify(jiraClient).createCertificateSigningRequestTicket(requestId, csr.request) + verify(jiraClient).createCertificateSigningRequestTicket(CertificationRequestData(requestId, csr.request)) verify(certificationRequestStorage).markRequestTicketCreated(requestId) } @@ -133,8 +133,8 @@ class JiraCsrHandlerTest : TestBase() { // Test. jiraCsrHandler.processRequests() - verify(jiraClient).createCertificateSigningRequestTicket(id1, csr1.request) - verify(jiraClient).createCertificateSigningRequestTicket(id2, csr2.request) + verify(jiraClient).createCertificateSigningRequestTicket(CertificationRequestData(id1, csr1.request)) + verify(jiraClient).createCertificateSigningRequestTicket(CertificationRequestData(id2, csr2.request)) verify(certificationRequestStorage).markRequestTicketCreated(id1) verify(certificationRequestStorage).markRequestTicketCreated(id2) @@ -145,7 +145,7 @@ class JiraCsrHandlerTest : TestBase() { // Verify jira client get the correct call. verify(jiraClient).updateRejectedRequest(id2) - verify(jiraClient, never()).updateDoneCertificateSigningRequest(any(), any()) + verify(jiraClient, never()).transitRequestStatusToDone(any(), anyOrNull()) // Sign request 1 val certPath = mock() @@ -156,6 +156,6 @@ class JiraCsrHandlerTest : TestBase() { jiraCsrHandler.processRequests() // Update signed request should be called. - verify(jiraClient).updateDoneCertificateSigningRequest(id1, certPath) + verify(jiraClient).transitRequestStatusToDone(eq(id1), anyOrNull()) } }