mirror of
https://github.com/corda/corda.git
synced 2025-02-27 11:36:42 +00:00
Merged in pat-doorman-status (pull request #27)
Doorman server status * added doorman server status for monitoring * minor changes * Addressed PR issues Fixed project dependency after rebased from corda master Approved-by: Shams Asari <shams.asari@r3.com>
This commit is contained in:
parent
6bb47e1214
commit
523c37079e
@ -41,6 +41,7 @@ dependencies {
|
|||||||
|
|
||||||
compile project(":core")
|
compile project(":core")
|
||||||
compile project(":node")
|
compile project(":node")
|
||||||
|
compile project(":node-api")
|
||||||
testCompile project(":test-utils")
|
testCompile project(":test-utils")
|
||||||
|
|
||||||
// Log4J: logging framework (with SLF4J bindings)
|
// Log4J: logging framework (with SLF4J bindings)
|
||||||
|
@ -5,8 +5,8 @@ import com.typesafe.config.Config
|
|||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
import com.typesafe.config.ConfigParseOptions
|
||||||
import net.corda.core.div
|
import net.corda.core.div
|
||||||
import net.corda.node.services.config.getOrElse
|
import net.corda.nodeapi.config.getOrElse
|
||||||
import net.corda.node.services.config.getValue
|
import net.corda.nodeapi.config.getValue
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -30,10 +30,10 @@ class DoormanParameters(vararg args: String) {
|
|||||||
private val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
|
private val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
|
||||||
val keystorePath: Path by config.getOrElse { basedir / "certificates" / "caKeystore.jks" }
|
val keystorePath: Path by config.getOrElse { basedir / "certificates" / "caKeystore.jks" }
|
||||||
val rootStorePath: Path by config.getOrElse { basedir / "certificates" / "rootCAKeystore.jks" }
|
val rootStorePath: Path by config.getOrElse { basedir / "certificates" / "rootCAKeystore.jks" }
|
||||||
val keystorePassword: String? by config.getOrElse { null }
|
val keystorePassword: String? by config
|
||||||
val caPrivateKeyPassword: String? by config.getOrElse { null }
|
val caPrivateKeyPassword: String? by config
|
||||||
val rootKeystorePassword: String? by config.getOrElse { null }
|
val rootKeystorePassword: String? by config
|
||||||
val rootPrivateKeyPassword: String? by config.getOrElse { null }
|
val rootPrivateKeyPassword: String? by config
|
||||||
val host: String by config
|
val host: String by config
|
||||||
val port: Int by config
|
val port: Int by config
|
||||||
val dataSourceProperties: Properties by config
|
val dataSourceProperties: Properties by config
|
||||||
|
@ -8,6 +8,7 @@ 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
|
||||||
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
@ -25,7 +26,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 DoormanWebService(val intermediateCACertAndKey: CACertAndKey, val rootCert: Certificate, val storage: CertificationRequestStorage) {
|
class DoormanWebService(val intermediateCACertAndKey: CACertAndKey, val rootCert: Certificate, val storage: CertificationRequestStorage, val serverStatus: DoormanServerStatus) {
|
||||||
@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.
|
||||||
@ -81,4 +82,11 @@ class DoormanWebService(val intermediateCACertAndKey: CACertAndKey, val rootCert
|
|||||||
is CertificateResponse.Unauthorised -> status(UNAUTHORIZED).entity(response.message)
|
is CertificateResponse.Unauthorised -> status(UNAUTHORIZED).entity(response.message)
|
||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("status")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
fun status(): Response {
|
||||||
|
return ok(ObjectMapper().writeValueAsString(serverStatus)).build()
|
||||||
|
}
|
||||||
}
|
}
|
@ -36,6 +36,7 @@ import java.lang.Thread.sleep
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
|
import java.time.Instant
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@ -44,8 +45,9 @@ 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 DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CACertAndKey, val rootCACert: Certificate, val storage: CertificationRequestStorage) : Closeable {
|
class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CACertAndKey, val rootCACert: Certificate, val storage: CertificationRequestStorage) : Closeable {
|
||||||
|
val serverStatus = DoormanServerStatus()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val logger = loggerFor<DoormanServer>()
|
val logger = loggerFor<DoormanServer>()
|
||||||
}
|
}
|
||||||
@ -72,6 +74,31 @@ class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CACertAndKey,
|
|||||||
logger.info("Starting Doorman Web Services...")
|
logger.info("Starting Doorman Web Services...")
|
||||||
server.start()
|
server.start()
|
||||||
logger.info("Doorman Web Services started on $hostAndPort")
|
logger.info("Doorman Web Services started on $hostAndPort")
|
||||||
|
serverStatus.serverStartTime = Instant.now()
|
||||||
|
|
||||||
|
// Thread approving request periodically.
|
||||||
|
thread(name = "Request Approval Thread") {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
sleep(10.seconds.toMillis())
|
||||||
|
// TODO: Handle rejected request?
|
||||||
|
serverStatus.lastRequestCheckTime = Instant.now()
|
||||||
|
for (id in storage.getApprovedRequestIds()) {
|
||||||
|
storage.approveRequest(id) {
|
||||||
|
val request = JcaPKCS10CertificationRequest(request)
|
||||||
|
createServerCert(request.subject, request.publicKey, caCertAndKey,
|
||||||
|
if (ipAddress == hostName) listOf() else listOf(hostName), listOf(ipAddress))
|
||||||
|
}
|
||||||
|
logger.info("Approved request $id")
|
||||||
|
serverStatus.lastApprovalTime = Instant.now()
|
||||||
|
serverStatus.approvedRequests++
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Log the error and carry on.
|
||||||
|
logger.error("Error encountered when approving request.", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildServletContextHandler(): ServletContextHandler {
|
private fun buildServletContextHandler(): ServletContextHandler {
|
||||||
@ -79,7 +106,7 @@ class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CACertAndKey,
|
|||||||
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(DoormanWebService(caCertAndKey, rootCACert, storage))
|
register(DoormanWebService(caCertAndKey, rootCACert, storage, serverStatus))
|
||||||
}
|
}
|
||||||
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply {
|
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply {
|
||||||
initOrder = 0 // Initialise at server start
|
initOrder = 0 // Initialise at server start
|
||||||
@ -89,6 +116,11 @@ class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CACertAndKey,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DoormanServerStatus(var serverStartTime: Instant? = null,
|
||||||
|
var lastRequestCheckTime: Instant? = null,
|
||||||
|
var lastApprovalTime: Instant? = null,
|
||||||
|
var approvedRequests: Int = 0)
|
||||||
|
|
||||||
/** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */
|
/** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */
|
||||||
private fun readPassword(fmt: String): String {
|
private fun readPassword(fmt: String): String {
|
||||||
return if (System.console() != null) {
|
return if (System.console() != null) {
|
||||||
@ -178,21 +210,6 @@ private fun DoormanParameters.startDoorman() {
|
|||||||
JiraCertificateRequestStorage(requestStorage, jiraClient, jiraConfig.projectCode, jiraConfig.doneTransitionCode)
|
JiraCertificateRequestStorage(requestStorage, jiraClient, jiraConfig.projectCode, jiraConfig.doneTransitionCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daemon thread approving request periodically.
|
|
||||||
thread(name = "Request Approval Daemon") {
|
|
||||||
while (true) {
|
|
||||||
sleep(10.seconds.toMillis())
|
|
||||||
// TODO: Handle rejected request?
|
|
||||||
for (id in storage.getApprovedRequestIds()) {
|
|
||||||
storage.approveRequest(id) {
|
|
||||||
val request = JcaPKCS10CertificationRequest(request)
|
|
||||||
createServerCert(request.subject, request.publicKey, caCertAndKey,
|
|
||||||
if (ipAddress == hostName) listOf() else listOf(hostName), listOf(ipAddress))
|
|
||||||
}
|
|
||||||
logger.info("Approved request $id")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val doorman = DoormanServer(HostAndPort.fromParts(host, port), caCertAndKey, rootCACert, storage)
|
val doorman = DoormanServer(HostAndPort.fromParts(host, port), caCertAndKey, rootCACert, storage)
|
||||||
doorman.start()
|
doorman.start()
|
||||||
Runtime.getRuntime().addShutdownHook(thread(start = false) { doorman.close() })
|
Runtime.getRuntime().addShutdownHook(thread(start = false) { doorman.close() })
|
||||||
|
@ -28,3 +28,4 @@ include 'samples:network-visualiser'
|
|||||||
include 'samples:simm-valuation-demo'
|
include 'samples:simm-valuation-demo'
|
||||||
include 'samples:raft-notary-demo'
|
include 'samples:raft-notary-demo'
|
||||||
include 'samples:bank-of-corda-demo'
|
include 'samples:bank-of-corda-demo'
|
||||||
|
include 'doorman'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user