Introducing audit table for Doorman and Signing-Server (#69)

* Introducing audit table for Doorman and Signing-Server

* Addressing review comments

* Removing TODO

* Adding comment on auto-signing path and DOORMAN_SIGNATURE usage

* Fixing integration tests
This commit is contained in:
mkit 2017-10-19 15:07:59 +01:00 committed by GitHub
parent 8428f78821
commit e6ce42281f
12 changed files with 130 additions and 94 deletions

View File

@ -90,6 +90,9 @@ dependencies {
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:1.3.0"
// Hibernate audit plugin
compile "org.hibernate:hibernate-envers:5.2.11.Final"
// Unit testing helpers.
testCompile 'junit:junit:4.12'
testCompile "org.assertj:assertj-core:${assertj_version}"

View File

@ -3,6 +3,7 @@ package com.r3.corda.doorman
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
import com.r3.corda.doorman.DoormanServer.Companion.logger
import com.r3.corda.doorman.persistence.CertificationRequestStorage
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.persistence.PersistenceNodeInfoStorage
@ -215,7 +216,7 @@ private fun buildLocalSigner(parameters: DoormanParameters): Signer? {
private class ApproveAllCertificateRequestStorage(private val delegate: CertificationRequestStorage) : CertificationRequestStorage by delegate {
override fun saveRequest(rawRequest: PKCS10CertificationRequest): String {
val requestId = delegate.saveRequest(rawRequest)
approveRequest(requestId)
approveRequest(requestId, DOORMAN_SIGNATURE)
return requestId
}
}

View File

@ -1,6 +1,7 @@
package com.r3.corda.doorman.persistence
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.hibernate.envers.Audited
import java.security.cert.CertPath
import java.time.Instant
import javax.persistence.*
@ -16,6 +17,8 @@ interface CertificationRequestStorage {
/**
* Persist [PKCS10CertificationRequest] in storage for further approval if it's a valid request. If not then it will be automatically
* rejected and not subject to any approval process. In both cases a randomly generated request ID is returned.
* @param certificationData certificate request data to be persisted.
* @param createdBy authority (its identifier) creating this request.
*/
fun saveRequest(rawRequest: PKCS10CertificationRequest): String
@ -31,28 +34,32 @@ interface CertificationRequestStorage {
/**
* Approve the given request if it has not already been approved. Otherwise do nothing.
*
* @param requestId id of the certificate signing request
* @param approvedBy authority (its identifier) approving this request.
* @return True if the request has been approved and false otherwise.
*/
// TODO: Merge status changing methods.
fun approveRequest(requestId: String, approvedBy: String = DOORMAN_SIGNATURE): Boolean
fun approveRequest(requestId: String, approvedBy: String): Boolean
/**
* Reject the given request using the given reason.
* @param requestId id of the certificate signing request
* @param rejectBy authority (its identifier) rejecting this request.
* @param rejectReason brief description of the rejection reason
*/
fun rejectRequest(requestId: String, rejectedBy: String = DOORMAN_SIGNATURE, rejectReason: String)
fun rejectRequest(requestId: String, rejectedBy: String, rejectReason: String)
/**
* Store certificate path with [requestId], this will store the encoded [CertPath] and transit request statue to [RequestStatus.Signed].
*
* @param requestId id of the certificate signing request
* @param signedBy authority (its identifier) signing this request.
* @throws IllegalArgumentException if request is not found or not in Approved state.
*/
fun putCertificatePath(requestId: String, certificates: CertPath, signedBy: List<String> = listOf(DOORMAN_SIGNATURE))
fun putCertificatePath(requestId: String, certificates: CertPath, signedBy: List<String>)
}
@Entity
@Table(name = "certificate_signing_request", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash")))
// TODO: Use Hibernate Envers to audit the table instead of individual "changed_by"/"changed_at" columns.
class CertificateSigningRequest(
@Id
@Column(name = "request_id", length = 64)
@ -66,34 +73,23 @@ class CertificateSigningRequest(
@Column
var request: ByteArray = ByteArray(0),
@Column(name = "created_at")
var createdAt: Instant = Instant.now(),
@Column(name = "approved_at")
var approvedAt: Instant = Instant.now(),
@Column(name = "approved_by", length = 64)
var approvedBy: String? = null,
@Column
@Audited
@Column(name = "status")
@Enumerated(EnumType.STRING)
var status: RequestStatus = RequestStatus.New,
@Column(name = "signed_by", length = 512)
@Audited
@Column(name = "modified_by", length = 512)
@ElementCollection(targetClass = String::class, fetch = FetchType.EAGER)
var signedBy: List<String>? = null,
var modifiedBy: List<String> = emptyList(),
@Column(name = "signed_at")
var signedAt: Instant? = Instant.now(),
@Audited
@Column(name = "modified_at")
var modifiedAt: Instant? = Instant.now(),
@Column(name = "rejected_by", length = 64)
var rejectedBy: String? = null,
@Column(name = "rejected_at")
var rejectedAt: Instant? = Instant.now(),
@Column(name = "reject_reason", length = 256, nullable = true)
var rejectReason: String? = null,
@Audited
@Column(name = "remark", length = 256, nullable = true)
var remark: String? = null,
// TODO: The certificate data can have its own table.
@Embedded

View File

@ -30,8 +30,8 @@ class DBCertificateRequestStorage(private val database: CordaPersistence) : Cert
val publicKeyHash = certificates.certificates.first().publicKey.hash()
request!!.certificateData = CertificateData(publicKeyHash, certificates.encoded, CertificateStatus.VALID)
request.status = Signed
request.signedBy = signedBy
request.signedAt = Instant.now()
request.modifiedBy = signedBy
request.modifiedAt = Instant.now()
session.save(request)
}
}
@ -66,7 +66,8 @@ class DBCertificateRequestStorage(private val database: CordaPersistence) : Cert
requestId = requestId,
legalName = legalName.toString(),
request = request.encoded,
rejectReason = rejectReason,
remark = rejectReason,
modifiedBy = emptyList(),
status = if (rejectReason == null) New else Rejected
))
}
@ -81,8 +82,8 @@ class DBCertificateRequestStorage(private val database: CordaPersistence) : Cert
builder.equal(path.get<String>(CertificateSigningRequest::status.name), New))
}
if (request != null) {
request.approvedAt = Instant.now()
request.approvedBy = approvedBy
request.modifiedAt = Instant.now()
request.modifiedBy = listOf(approvedBy)
request.status = Approved
session.save(request)
approved = true
@ -97,10 +98,10 @@ class DBCertificateRequestStorage(private val database: CordaPersistence) : Cert
builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), requestId)
}
if (request != null) {
request.rejectReason = rejectReason
request.remark = rejectReason
request.status = Rejected
request.rejectedBy = rejectedBy
request.rejectedAt = Instant.now()
request.modifiedBy = listOf(rejectedBy)
request.modifiedAt = Instant.now()
session.save(request)
}
}

View File

@ -4,6 +4,7 @@ import com.r3.corda.doorman.JiraClient
import com.r3.corda.doorman.buildCertPath
import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestStorage
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.persistence.RequestStatus
import org.bouncycastle.pkcs.PKCS10CertificationRequest
@ -22,7 +23,9 @@ class DefaultCsrHandler(private val storage: CertificationRequestStorage, privat
private fun processRequest(requestId: String, request: PKCS10CertificationRequest) {
if (signer != null) {
val certs = signer.sign(request)
storage.putCertificatePath(requestId, certs)
// Since Doorman is deployed in the auto-signing mode (i.e. signer != null),
// we use DOORMAN_SIGNATURE as the signer.
storage.putCertificatePath(requestId, certs, listOf(DOORMAN_SIGNATURE))
}
}
@ -34,7 +37,7 @@ class DefaultCsrHandler(private val storage: CertificationRequestStorage, privat
val response = storage.getRequest(requestId)
return when (response?.status) {
RequestStatus.New, RequestStatus.Approved, null -> CertificateResponse.NotReady
RequestStatus.Rejected -> CertificateResponse.Unauthorised(response.rejectReason ?: "Unknown reason")
RequestStatus.Rejected -> CertificateResponse.Unauthorised(response.remark ?: "Unknown reason")
RequestStatus.Signed -> CertificateResponse.Ready(buildCertPath(response.certificateData?.certificatePath ?: throw IllegalArgumentException("Certificate should not be null.")))
}
}

View File

@ -22,7 +22,7 @@ class DefaultRequestProcessorTest {
val requestStorage: CertificationRequestStorage = mock {
on { getRequest("New") }.thenReturn(CertificateSigningRequest(status = RequestStatus.New))
on { getRequest("Signed") }.thenReturn(CertificateSigningRequest(status = RequestStatus.Signed, certificateData = CertificateData("", buildCertPath(cert.toX509Certificate()).encoded, CertificateStatus.VALID)))
on { getRequest("Rejected") }.thenReturn(CertificateSigningRequest(status = RequestStatus.Rejected, rejectReason = "Random reason"))
on { getRequest("Rejected") }.thenReturn(CertificateSigningRequest(status = RequestStatus.Rejected, remark = "Random reason"))
}
val signer: Signer = mock()
val requestProcessor = DefaultCsrHandler(requestStorage, signer)

View File

@ -1,8 +1,9 @@
package com.r3.corda.doorman.internal.persistence
import com.r3.corda.doorman.buildCertPath
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.persistence.CertificateSigningRequest
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.persistence.RequestStatus
import com.r3.corda.doorman.toX509Certificate
import net.corda.core.crypto.Crypto
@ -16,12 +17,14 @@ import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.hibernate.envers.AuditReaderFactory
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.security.KeyPair
import java.util.*
import kotlin.test.*
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
class DBCertificateRequestStorageTest {
private lateinit var storage: DBCertificateRequestStorage
@ -58,7 +61,7 @@ class DBCertificateRequestStorageTest {
// Certificate should be empty.
assertNull(storage.getRequest(requestId)!!.certificateData)
// Store certificate to DB.
val result = storage.approveRequest(requestId)
val result = storage.approveRequest(requestId, DOORMAN_SIGNATURE)
// Check request request has been approved
assertTrue(result)
// Check request is not ready yet.
@ -73,10 +76,10 @@ class DBCertificateRequestStorageTest {
val (request, _) = createRequest("LegalName")
// Add request to DB.
val requestId = storage.saveRequest(request)
storage.approveRequest(requestId)
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
// When subsequent approval is performed
val result = storage.approveRequest(requestId)
val result = storage.approveRequest(requestId, DOORMAN_SIGNATURE)
// Then check request has not been approved
assertFalse(result)
}
@ -91,20 +94,20 @@ class DBCertificateRequestStorageTest {
// Certificate should be empty.
assertNull(storage.getRequest(requestId)!!.certificateData)
// Store certificate to DB.
storage.approveRequest(requestId)
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
// Check request is not ready yet.
assertEquals(RequestStatus.Approved, storage.getRequest(requestId)!!.status)
// New request should be empty.
assertTrue(storage.getRequests(RequestStatus.New).isEmpty())
// Sign certificate
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
})
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
}, listOf(DOORMAN_SIGNATURE))
// Check request is ready
assertNotNull(storage.getRequest(requestId)!!.certificateData)
}
@ -115,18 +118,18 @@ class DBCertificateRequestStorageTest {
// Add request to DB.
val requestId = storage.saveRequest(csr)
// Store certificate to DB.
storage.approveRequest(requestId)
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
})
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
}, listOf(DOORMAN_SIGNATURE))
// Sign certificate
// When subsequent signature requested
assertFailsWith(IllegalArgumentException::class){
assertFailsWith(IllegalArgumentException::class) {
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
@ -134,16 +137,16 @@ class DBCertificateRequestStorageTest {
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
})
}, listOf(DOORMAN_SIGNATURE))
}
}
@Test
fun `reject request`() {
val requestId = storage.saveRequest(createRequest("BankA").first)
storage.rejectRequest(requestId, rejectReason = "Because I said so!")
storage.rejectRequest(requestId, DOORMAN_SIGNATURE, "Because I said so!")
assertThat(storage.getRequests(RequestStatus.New)).isEmpty()
assertThat(storage.getRequest(requestId)!!.rejectReason).isEqualTo("Because I said so!")
assertThat(storage.getRequest(requestId)!!.remark).isEqualTo("Because I said so!")
}
@Test
@ -153,30 +156,51 @@ class DBCertificateRequestStorageTest {
val requestId2 = storage.saveRequest(createRequest("BankA").first)
assertThat(storage.getRequests(RequestStatus.New).map { it.requestId }).containsOnly(requestId1)
assertEquals(RequestStatus.Rejected, storage.getRequest(requestId2)!!.status)
assertThat(storage.getRequest(requestId2)!!.rejectReason).containsIgnoringCase("duplicate")
assertThat(storage.getRequest(requestId2)!!.remark).containsIgnoringCase("duplicate")
// Make sure the first request is processed properly
storage.approveRequest(requestId1)
storage.approveRequest(requestId1, DOORMAN_SIGNATURE)
assertThat(storage.getRequest(requestId1)!!.status).isEqualTo(RequestStatus.Approved)
}
@Test
fun `request with the same legal name as a previously approved request`() {
val requestId1 = storage.saveRequest(createRequest("BankA").first)
storage.approveRequest(requestId1)
storage.approveRequest(requestId1, DOORMAN_SIGNATURE)
val requestId2 = storage.saveRequest(createRequest("BankA").first)
assertThat(storage.getRequest(requestId2)!!.rejectReason).containsIgnoringCase("duplicate")
assertThat(storage.getRequest(requestId2)!!.remark).containsIgnoringCase("duplicate")
}
@Test
fun `request with the same legal name as a previously rejected request`() {
val requestId1 = storage.saveRequest(createRequest("BankA").first)
storage.rejectRequest(requestId1, rejectReason = "Because I said so!")
storage.rejectRequest(requestId1, DOORMAN_SIGNATURE, "Because I said so!")
val requestId2 = storage.saveRequest(createRequest("BankA").first)
assertThat(storage.getRequests(RequestStatus.New).map { it.requestId }).containsOnly(requestId2)
storage.approveRequest(requestId2)
storage.approveRequest(requestId2, DOORMAN_SIGNATURE)
assertThat(storage.getRequest(requestId2)!!.status).isEqualTo(RequestStatus.Approved)
}
@Test
fun `audit data is available for CSRs`() {
// given
val approver = "APPROVER"
// when
val requestId = storage.saveRequest(createRequest("BankA").first)
storage.approveRequest(requestId, approver)
// then
persistence.transaction {
val auditReader = AuditReaderFactory.get(persistence.entityManagerFactory.createEntityManager())
val newRevision = auditReader.find(CertificateSigningRequest::class.java, requestId, 1)
assertEquals(RequestStatus.New, newRevision.status)
assertTrue(newRevision.modifiedBy.isEmpty())
val approvedRevision = auditReader.find(CertificateSigningRequest::class.java, requestId, 2)
assertEquals(RequestStatus.Approved, approvedRevision.status)
assertEquals(approver, approvedRevision.modifiedBy.first())
}
}
private fun createRequest(legalName: String): Pair<PKCS10CertificationRequest, KeyPair> {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(organisation = legalName, locality = "London", country = "GB"), "my@mail.com", keyPair)

View File

@ -3,6 +3,7 @@ package com.r3.corda.doorman.internal.persistence
import com.r3.corda.doorman.buildCertPath
import com.r3.corda.doorman.hash
import com.r3.corda.doorman.persistence.*
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.toX509Certificate
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sha256
@ -79,11 +80,11 @@ class PersistenceNodeInfoStorageTest {
val request = X509Utilities.createCertificateSigningRequest(nodeInfo.legalIdentities.first().name, "my@mail.com", keyPair)
val requestId = requestStorage.saveRequest(request)
requestStorage.approveRequest(requestId)
requestStorage.approveRequest(requestId, DOORMAN_SIGNATURE)
assertNull(nodeInfoStorage.getCertificatePath(keyPair.public.hash()))
requestStorage.putCertificatePath(requestId, buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()))
requestStorage.putCertificatePath(requestId, buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()), listOf(DOORMAN_SIGNATURE))
val storedCertPath = nodeInfoStorage.getCertificatePath(keyPair.public.hash())
assertNotNull(storedCertPath)

View File

@ -81,6 +81,9 @@ dependencies {
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:1.3.0"
// Hibernate audit plugin
compile "org.hibernate:hibernate-envers:5.2.11.Final"
// Unit testing helpers.
testCompile 'junit:junit:4.12'
testCompile "org.assertj:assertj-core:${assertj_version}"

View File

@ -8,6 +8,10 @@ import com.r3.corda.doorman.buildCertPath
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.startDoorman
import com.r3.corda.doorman.toX509Certificate
import com.r3.corda.signing.hsm.HsmSigner
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData
import com.r3.corda.signing.persistence.DBCertificateRequestStorage
import com.r3.corda.signing.persistence.SigningServerSchemaService
import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort
@ -17,10 +21,6 @@ import net.corda.node.utilities.X509Utilities
import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NetworkRegistrationHelper
import com.r3.corda.signing.hsm.HsmSigner
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData
import com.r3.corda.signing.persistence.DBCertificateRequestStorage
import com.r3.corda.signing.persistence.SigningServerSchemaService
import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.CHARLIE
@ -86,10 +86,10 @@ class SigningServiceIntegrationTest {
@Test
fun `Signing service communicates with Doorman`() {
//Start doorman server
val database = configureDatabase(makeTestDataSourceProperties(), null, { DoormanSchemaService() }, createIdentityService = {
val database = configureDatabase(makeTestDataSourceProperties(), null, {
// Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException()
})
}, DoormanSchemaService())
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true)
// Start Corda network registration.
@ -99,10 +99,10 @@ class SigningServiceIntegrationTest {
whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
}
val signingServiceStorage = DBCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), { SigningServerSchemaService() }, createIdentityService = {
val signingServiceStorage = DBCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), {
// Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException()
}))
}, SigningServerSchemaService()))
val hsmSigner = givenSignerSigningAllRequests(signingServiceStorage)
// Poll the database for approved requests
@ -133,10 +133,10 @@ class SigningServiceIntegrationTest {
@Ignore
fun `DEMO - Create CSR and poll`() {
//Start doorman server
val database = configureDatabase(makeTestDataSourceProperties(), null, { DoormanSchemaService() }, createIdentityService = {
val database = configureDatabase(makeTestDataSourceProperties(), null, {
// Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException()
})
}, SigningServerSchemaService())
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true)
thread(start = true, isDaemon = true) {
@ -150,7 +150,7 @@ class SigningServiceIntegrationTest {
val config = testNodeConfiguration(
baseDirectory = tempFolder.root.toPath(),
myLegalName = when(it) {
myLegalName = when (it) {
1 -> ALICE.name
2 -> BOB.name
3 -> CHARLIE.name

View File

@ -2,6 +2,7 @@ package com.r3.corda.signing.persistence
import net.corda.node.utilities.CordaPersistence
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.hibernate.envers.Audited
import java.security.cert.CertPath
import java.sql.Connection
import java.time.Instant
@ -34,16 +35,19 @@ class DBCertificateRequestStorage(private val database: CordaPersistence) : Cert
@Column(nullable = true)
var certificatePath: ByteArray? = null,
@Column(name = "signed_by", length = 512)
@ElementCollection(targetClass = String::class, fetch = FetchType.EAGER)
var signedBy: List<String>? = null,
@Column(name = "signed_at")
var signedAt: Instant? = Instant.now(),
@Audited
@Column(name = "status")
@Enumerated(EnumType.STRING)
var status: Status = Status.Approved
var status: Status = Status.Approved,
@Audited
@Column(name = "modified_by", length = 512)
@ElementCollection(targetClass = String::class, fetch = FetchType.EAGER)
var modifiedBy: List<String> = emptyList(),
@Audited
@Column(name = "modified_at")
var modifiedAt: Instant? = Instant.now()
)
override fun getApprovedRequests(): List<ApprovedCertificateRequestData> {
@ -63,8 +67,8 @@ class DBCertificateRequestStorage(private val database: CordaPersistence) : Cert
val now = Instant.now()
request.certificatePath = it.certPath?.encoded
request.status = Status.Signed
request.signedAt = now
request.signedBy = signers
request.modifiedAt = now
request.modifiedBy = signers
session.update(request)
}
}

View File

@ -88,7 +88,7 @@ class DBCertificateRequestStorageTest {
val request = getRequestById(it.requestId)
assertNotNull(request)
assertEquals(Status.Signed, request?.status)
assertEquals(signers.toString(), request?.signedBy.toString())
assertEquals(signers.toString(), request?.modifiedBy.toString())
assertNotNull(request?.certificatePath)
}
}