mirror of
https://github.com/corda/corda.git
synced 2025-02-07 11:30:22 +00:00
ENT-1821 - Doorman failed with parallel requests (#827)
* attempt to fix race condition by introducing thread lock
This commit is contained in:
parent
2a07537f7b
commit
6a783124bf
@ -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') {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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<String, Int>()
|
||||
|
||||
fun getApprovedRequests(): List<ApprovedRequest> {
|
||||
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<RejectedRequest> {
|
||||
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<String, InputStream>? = 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<String, InputStream>? = 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<String, InputStream>) {
|
||||
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 <T : Any> Iterable<T>.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"
|
||||
}
|
||||
|
@ -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) }
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<CertPath>()
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user