mirror of
https://github.com/corda/corda.git
synced 2024-12-29 09:18:58 +00:00
Rename netpermission to doorman
This commit is contained in:
parent
0c1a6aad6b
commit
6ff8d73402
@ -13,9 +13,9 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task buildCertSignerJAR(type: FatCapsule, dependsOn: 'jar') {
|
task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') {
|
||||||
applicationClass 'com.r3.corda.netpermission.MainKt'
|
applicationClass 'com.r3.corda.doorman.MainKt'
|
||||||
archiveName 'certSigner.jar'
|
archiveName 'doorman.jar'
|
||||||
|
|
||||||
capsuleManifest {
|
capsuleManifest {
|
||||||
systemProperties['log4j.configuration'] = 'log4j2.xml'
|
systemProperties['log4j.configuration'] = 'log4j2.xml'
|
@ -1,8 +1,8 @@
|
|||||||
package com.r3.corda.netpermission.internal
|
package com.r3.corda.doorman
|
||||||
|
|
||||||
import com.r3.corda.netpermission.internal.persistence.CertificateResponse
|
import com.r3.corda.doorman.persistence.CertificateResponse
|
||||||
import com.r3.corda.netpermission.internal.persistence.CertificationData
|
import com.r3.corda.doorman.persistence.CertificationRequestData
|
||||||
import com.r3.corda.netpermission.internal.persistence.CertificationRequestStorage
|
import com.r3.corda.doorman.persistence.CertificationRequestStorage
|
||||||
import net.corda.core.crypto.X509Utilities.CACertAndKey
|
import net.corda.core.crypto.X509Utilities.CACertAndKey
|
||||||
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
|
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||||
import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||||
@ -25,7 +25,7 @@ import javax.ws.rs.core.Response.Status.UNAUTHORIZED
|
|||||||
* Provides functionality for asynchronous submission of certificate signing requests and retrieval of the results.
|
* Provides functionality for asynchronous submission of certificate signing requests and retrieval of the results.
|
||||||
*/
|
*/
|
||||||
@Path("")
|
@Path("")
|
||||||
class CertificateSigningService(val intermediateCACertAndKey: CACertAndKey, val rootCert: Certificate, val storage: CertificationRequestStorage) {
|
class DoormanWebService(val intermediateCACertAndKey: CACertAndKey, val rootCert: Certificate, val storage: CertificationRequestStorage) {
|
||||||
@Context lateinit var request: HttpServletRequest
|
@Context lateinit var request: HttpServletRequest
|
||||||
/**
|
/**
|
||||||
* Accept stream of [PKCS10CertificationRequest] from user and persists in [CertificationRequestStorage] for approval.
|
* Accept stream of [PKCS10CertificationRequest] from user and persists in [CertificationRequestStorage] for approval.
|
||||||
@ -42,7 +42,7 @@ class CertificateSigningService(val intermediateCACertAndKey: CACertAndKey, val
|
|||||||
// TODO: Certificate signing request verifications.
|
// TODO: Certificate signing request verifications.
|
||||||
// TODO: Use jira api / slack bot to semi automate the approval process?
|
// TODO: Use jira api / slack bot to semi automate the approval process?
|
||||||
// TODO: Acknowledge to user we have received the request via email?
|
// TODO: Acknowledge to user we have received the request via email?
|
||||||
val requestId = storage.saveRequest(CertificationData(request.remoteHost, request.remoteAddr, certificationRequest))
|
val requestId = storage.saveRequest(CertificationRequestData(request.remoteHost, request.remoteAddr, certificationRequest))
|
||||||
return ok(requestId).build()
|
return ok(requestId).build()
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
|||||||
package com.r3.corda.netpermission
|
package com.r3.corda.doorman
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.r3.corda.netpermission.internal.CertificateSigningService
|
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
|
||||||
import com.r3.corda.netpermission.internal.persistence.DBCertificateRequestStorage
|
|
||||||
import joptsimple.ArgumentAcceptingOptionSpec
|
import joptsimple.ArgumentAcceptingOptionSpec
|
||||||
import joptsimple.OptionParser
|
import joptsimple.OptionParser
|
||||||
import net.corda.core.crypto.X509Utilities
|
import net.corda.core.crypto.X509Utilities
|
||||||
@ -30,20 +29,22 @@ import kotlin.system.exitProcess
|
|||||||
* The server will require keystorePath, keystore password and key password via command line input.
|
* The server will require keystorePath, keystore password and key password via command line input.
|
||||||
* The Intermediate CA certificate,Intermediate CA private key and Root CA Certificate should use alias name specified in [X509Utilities]
|
* The Intermediate CA certificate,Intermediate CA private key and Root CA Certificate should use alias name specified in [X509Utilities]
|
||||||
*/
|
*/
|
||||||
class CertificateSigningServer(val webServerAddr: HostAndPort, val certSigningService: CertificateSigningService) : Closeable {
|
class DoormanServer(val webServerAddr: HostAndPort, val doormanWebService: DoormanWebService) : Closeable {
|
||||||
companion object {
|
companion object {
|
||||||
val log = loggerFor<CertificateSigningServer>()
|
val log = loggerFor<DoormanServer>()
|
||||||
fun Server.hostAndPort(): HostAndPort {
|
|
||||||
val connector = server.connectors.first() as ServerConnector
|
|
||||||
return HostAndPort.fromParts(connector.host, connector.localPort)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val server: Server = initWebServer()
|
private val server: Server = initWebServer()
|
||||||
|
val hostAndPort: HostAndPort get() = server.connectors
|
||||||
|
.map { it as? ServerConnector }
|
||||||
|
.filterNotNull()
|
||||||
|
.map { HostAndPort.fromParts(it.host, it.localPort) }
|
||||||
|
.first()
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
log.info("Shutting down CertificateSigningService...")
|
log.info("Shutting down CertificateSigningService...")
|
||||||
server.stop()
|
server.stop()
|
||||||
|
server.join()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initWebServer(): Server {
|
private fun initWebServer(): Server {
|
||||||
@ -53,7 +54,7 @@ class CertificateSigningServer(val webServerAddr: HostAndPort, val certSigningSe
|
|||||||
addHandler(buildServletContextHandler())
|
addHandler(buildServletContextHandler())
|
||||||
}
|
}
|
||||||
start()
|
start()
|
||||||
log.info("CertificateSigningService started on ${server.hostAndPort()}")
|
log.info("CertificateSigningService started on $hostAndPort")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ class CertificateSigningServer(val webServerAddr: HostAndPort, val certSigningSe
|
|||||||
contextPath = "/"
|
contextPath = "/"
|
||||||
val resourceConfig = ResourceConfig().apply {
|
val resourceConfig = ResourceConfig().apply {
|
||||||
// Add your API provider classes (annotated for JAX-RS) here
|
// Add your API provider classes (annotated for JAX-RS) here
|
||||||
register(certSigningService)
|
register(doormanWebService)
|
||||||
}
|
}
|
||||||
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply {
|
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply {
|
||||||
initOrder = 0 // Initialise at server start
|
initOrder = 0 // Initialise at server start
|
||||||
@ -79,7 +80,7 @@ object ParamsSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val log = CertificateSigningServer.log
|
val log = DoormanServer.log
|
||||||
log.info("Starting certificate signing server.")
|
log.info("Starting certificate signing server.")
|
||||||
try {
|
try {
|
||||||
ParamsSpec.parser.parse(*args)
|
ParamsSpec.parser.parse(*args)
|
||||||
@ -97,9 +98,8 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
// Create DB connection.
|
// Create DB connection.
|
||||||
val (datasource, database) = configureDatabase(config.getProperties("dataSourceProperties"))
|
val (datasource, database) = configureDatabase(config.getProperties("dataSourceProperties"))
|
||||||
|
|
||||||
val storage = DBCertificateRequestStorage(database)
|
val storage = DBCertificateRequestStorage(database)
|
||||||
val service = CertificateSigningService(intermediateCACertAndKey, rootCA, storage)
|
val service = DoormanWebService(intermediateCACertAndKey, rootCA, storage)
|
||||||
|
|
||||||
// Background thread approving all request periodically.
|
// Background thread approving all request periodically.
|
||||||
var stopSigner = false
|
var stopSigner = false
|
||||||
@ -123,14 +123,12 @@ fun main(args: Array<String>) {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateSigningServer(HostAndPort.fromParts(config.getString("host"), config.getInt("port")), service).use {
|
DoormanServer(HostAndPort.fromParts(config.getString("host"), config.getInt("port")), service).use {
|
||||||
Runtime.getRuntime().addShutdownHook(thread(false) {
|
Runtime.getRuntime().addShutdownHook(thread(false) {
|
||||||
stopSigner = true
|
stopSigner = true
|
||||||
certSinger?.join()
|
certSinger?.join()
|
||||||
it.close()
|
|
||||||
datasource.close()
|
datasource.close()
|
||||||
})
|
})
|
||||||
it.server.join()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.r3.corda.netpermission.internal.persistence
|
package com.r3.corda.doorman.persistence
|
||||||
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
@ -11,12 +11,12 @@ interface CertificationRequestStorage {
|
|||||||
* Persist [certificationData] in storage for further approval if it's a valid request. If not then it will be automically
|
* Persist [certificationData] in storage for further approval if it's a valid request. If not then it will be automically
|
||||||
* rejected and not subject to any approval process. In both cases a randomly generated request ID is returned.
|
* rejected and not subject to any approval process. In both cases a randomly generated request ID is returned.
|
||||||
*/
|
*/
|
||||||
fun saveRequest(certificationData: CertificationData): String
|
fun saveRequest(certificationData: CertificationRequestData): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve certificate singing request and Host/IP information using [requestId].
|
* Retrieve certificate singing request and Host/IP information using [requestId].
|
||||||
*/
|
*/
|
||||||
fun getRequest(requestId: String): CertificationData?
|
fun getRequest(requestId: String): CertificationRequestData?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the response for a previously saved request with ID [requestId].
|
* Return the response for a previously saved request with ID [requestId].
|
||||||
@ -26,7 +26,7 @@ interface CertificationRequestStorage {
|
|||||||
/**
|
/**
|
||||||
* Approve the given request by generating and storing a new certificate using the provided generator.
|
* Approve the given request by generating and storing a new certificate using the provided generator.
|
||||||
*/
|
*/
|
||||||
fun approveRequest(requestId: String, certificateGenerator: (CertificationData) -> Certificate)
|
fun approveRequest(requestId: String, certificateGenerator: (CertificationRequestData) -> Certificate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reject the given request using the given reason.
|
* Reject the given request using the given reason.
|
||||||
@ -40,7 +40,7 @@ interface CertificationRequestStorage {
|
|||||||
fun getPendingRequestIds(): List<String>
|
fun getPendingRequestIds(): List<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CertificationData(val hostName: String, val ipAddress: String, val request: PKCS10CertificationRequest)
|
data class CertificationRequestData(val hostName: String, val ipAddress: String, val request: PKCS10CertificationRequest)
|
||||||
|
|
||||||
sealed class CertificateResponse {
|
sealed class CertificateResponse {
|
||||||
object NotReady : CertificateResponse()
|
object NotReady : CertificateResponse()
|
@ -1,4 +1,4 @@
|
|||||||
package com.r3.corda.netpermission.internal.persistence
|
package com.r3.corda.doorman.persistence
|
||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.commonName
|
import net.corda.core.crypto.commonName
|
||||||
@ -29,7 +29,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveRequest(certificationData: CertificationData): String {
|
override fun saveRequest(certificationData: CertificationRequestData): String {
|
||||||
val legalName = certificationData.request.subject.commonName
|
val legalName = certificationData.request.subject.commonName
|
||||||
val requestId = SecureHash.randomSHA256().toString()
|
val requestId = SecureHash.randomSHA256().toString()
|
||||||
databaseTransaction(database) {
|
databaseTransaction(database) {
|
||||||
@ -83,7 +83,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun approveRequest(requestId: String, certificateGenerator: (CertificationData) -> Certificate) {
|
override fun approveRequest(requestId: String, certificateGenerator: (CertificationRequestData) -> Certificate) {
|
||||||
databaseTransaction(database) {
|
databaseTransaction(database) {
|
||||||
val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
|
val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
@ -109,7 +109,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRequest(requestId: String): CertificationData? {
|
override fun getRequest(requestId: String): CertificationRequestData? {
|
||||||
return databaseTransaction(database) {
|
return databaseTransaction(database) {
|
||||||
singleRequestWhere { DataTable.requestId eq requestId }
|
singleRequestWhere { DataTable.requestId eq requestId }
|
||||||
}
|
}
|
||||||
@ -121,10 +121,10 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun singleRequestWhere(where: SqlExpressionBuilder.() -> Op<Boolean>): CertificationData? {
|
private fun singleRequestWhere(where: SqlExpressionBuilder.() -> Op<Boolean>): CertificationRequestData? {
|
||||||
return DataTable
|
return DataTable
|
||||||
.select(where)
|
.select(where)
|
||||||
.map { CertificationData(it[DataTable.hostName], it[DataTable.ipAddress], deserializeFromBlob(it[DataTable.request])) }
|
.map { CertificationRequestData(it[DataTable.hostName], it[DataTable.ipAddress], deserializeFromBlob(it[DataTable.request])) }
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package com.r3.corda.netpermission
|
package com.r3.corda.doorman
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
import com.r3.corda.netpermission.CertificateSigningServer.Companion.hostAndPort
|
import com.r3.corda.doorman.persistence.CertificateResponse
|
||||||
import com.r3.corda.netpermission.internal.CertificateSigningService
|
import com.r3.corda.doorman.persistence.CertificationRequestData
|
||||||
import com.r3.corda.netpermission.internal.persistence.CertificateResponse
|
import com.r3.corda.doorman.persistence.CertificationRequestStorage
|
||||||
import com.r3.corda.netpermission.internal.persistence.CertificationData
|
|
||||||
import com.r3.corda.netpermission.internal.persistence.CertificationRequestStorage
|
|
||||||
import net.corda.core.crypto.CertificateStream
|
import net.corda.core.crypto.CertificateStream
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.X509Utilities
|
import net.corda.core.crypto.X509Utilities
|
||||||
@ -27,18 +25,18 @@ import java.util.zip.ZipInputStream
|
|||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class CertificateSigningServiceTest {
|
class DoormanServiceTest {
|
||||||
private val rootCA = X509Utilities.createSelfSignedCACert("Corda Node Root CA")
|
private val rootCA = X509Utilities.createSelfSignedCACert("Corda Node Root CA")
|
||||||
private val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA")
|
private val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA")
|
||||||
private lateinit var signingServer: CertificateSigningServer
|
private lateinit var doormanServer: DoormanServer
|
||||||
|
|
||||||
private fun startSigningServer(storage: CertificationRequestStorage) {
|
private fun startSigningServer(storage: CertificationRequestStorage) {
|
||||||
signingServer = CertificateSigningServer(HostAndPort.fromParts("localhost", 0), CertificateSigningService(intermediateCA, rootCA.certificate, storage))
|
doormanServer = DoormanServer(HostAndPort.fromParts("localhost", 0), DoormanWebService(intermediateCA, rootCA.certificate, storage))
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun close() {
|
fun close() {
|
||||||
signingServer.close()
|
doormanServer.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -74,8 +72,8 @@ class CertificateSigningServiceTest {
|
|||||||
}
|
}
|
||||||
on { approveRequest(eq(id), any()) }.then {
|
on { approveRequest(eq(id), any()) }.then {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val certGen = it.arguments[1] as ((CertificationData) -> Certificate)
|
val certGen = it.arguments[1] as ((CertificationRequestData) -> Certificate)
|
||||||
val request = CertificationData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair))
|
val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair))
|
||||||
certificateStore[id] = certGen(request)
|
certificateStore[id] = certGen(request)
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
@ -123,7 +121,7 @@ class CertificateSigningServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun submitRequest(request: PKCS10CertificationRequest): String {
|
private fun submitRequest(request: PKCS10CertificationRequest): String {
|
||||||
val conn = URL("http://${signingServer.server.hostAndPort()}/api/certificate").openConnection() as HttpURLConnection
|
val conn = URL("http://${doormanServer.hostAndPort}/api/certificate").openConnection() as HttpURLConnection
|
||||||
conn.doOutput = true
|
conn.doOutput = true
|
||||||
conn.requestMethod = "POST"
|
conn.requestMethod = "POST"
|
||||||
conn.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM)
|
conn.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM)
|
||||||
@ -132,7 +130,7 @@ class CertificateSigningServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun pollForResponse(id: String): PollResponse {
|
private fun pollForResponse(id: String): PollResponse {
|
||||||
val url = URL("http://${signingServer.server.hostAndPort()}/api/certificate/$id")
|
val url = URL("http://${doormanServer.hostAndPort}/api/certificate/$id")
|
||||||
val conn = url.openConnection() as HttpURLConnection
|
val conn = url.openConnection() as HttpURLConnection
|
||||||
conn.requestMethod = "GET"
|
conn.requestMethod = "GET"
|
||||||
|
|
@ -1,5 +1,8 @@
|
|||||||
package com.r3.corda.netpermission.internal.persistence
|
package com.r3.corda.doorman.internal.persistence
|
||||||
|
|
||||||
|
import com.r3.corda.doorman.persistence.CertificateResponse
|
||||||
|
import com.r3.corda.doorman.persistence.CertificationRequestData
|
||||||
|
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
|
||||||
import net.corda.core.crypto.X509Utilities
|
import net.corda.core.crypto.X509Utilities
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.node.makeTestDataSourceProperties
|
import net.corda.testing.node.makeTestDataSourceProperties
|
||||||
@ -119,9 +122,9 @@ class DBCertificateRequestStorageTest {
|
|||||||
assertThat(response.message).contains(",")
|
assertThat(response.message).contains(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRequest(legalName: String): Pair<CertificationData, KeyPair> {
|
private fun createRequest(legalName: String): Pair<CertificationRequestData, KeyPair> {
|
||||||
val keyPair = X509Utilities.generateECDSAKeyPairForSSL()
|
val keyPair = X509Utilities.generateECDSAKeyPairForSSL()
|
||||||
val request = CertificationData(
|
val request = CertificationRequestData(
|
||||||
"hostname",
|
"hostname",
|
||||||
"0.0.0.0",
|
"0.0.0.0",
|
||||||
X509Utilities.createCertificateSigningRequest(legalName, "London", "admin@test.com", keyPair))
|
X509Utilities.createCertificateSigningRequest(legalName, "London", "admin@test.com", keyPair))
|
@ -7,7 +7,7 @@ include 'core'
|
|||||||
include 'node'
|
include 'node'
|
||||||
include 'node:capsule'
|
include 'node:capsule'
|
||||||
include 'client'
|
include 'client'
|
||||||
include 'netpermission'
|
include 'doorman'
|
||||||
include 'experimental'
|
include 'experimental'
|
||||||
include 'experimental:sandbox'
|
include 'experimental:sandbox'
|
||||||
include 'test-utils'
|
include 'test-utils'
|
||||||
|
Loading…
Reference in New Issue
Block a user