diff --git a/backend/app/.env.properties b/backend/app/.env.properties
index c6f5749..d7a12ca 100644
--- a/backend/app/.env.properties
+++ b/backend/app/.env.properties
@@ -32,3 +32,9 @@ VALTIMO_OPENKLANT_SECRET=e09b8bc5-5831-4618-ab28-41411304309d
#org.openapitools.client.baseUrl=${BASEURL}
org.openapitools.client.baseUrl=wdwwer
+ROTTERDAM_XENTIAL_ACCOUNT_NAME=gzac
+ROTTERDAM_XENTIAL_ACCOUNT_PASSWORD=12345
+
+ROTTERDAM_ESB_SERVER_CERTIFICATE_FILENAME=/Users/paul/Documents/rotterdam/xential/test.gemeente-rotterdam.gzac.cloud_20241023.pem
+ROTTERDAM_ESB_CLIENT_PRIVATE_KEY_FILENAME=/Users/paul/Documents/rotterdam/xential/private.key
+ROTTERDAM_ESB_CLIENT_CERTIFICATE_FILENAME=/Users/paul/Documents/rotterdam/xential/test.gemeente-rotterdam.gzac.cloud_20241023.pem
diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt
index a2afdcb..7ccf68b 100644
--- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt
+++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt
@@ -18,6 +18,7 @@ package com.ritense.valtimo.xential.autoconfiguration
import com.ritense.documentenapi.client.DocumentenApiClient
import com.ritense.plugin.service.PluginService
+import com.ritense.valtimo.contract.authentication.UserManagementService
import com.ritense.valtimo.contract.config.LiquibaseMasterChangeLogLocation
import com.ritense.valtimo.xential.plugin.XentialPluginFactory
import com.ritense.valtimo.xential.repository.XentialTokenRepository
@@ -25,17 +26,13 @@ import com.ritense.valtimo.xential.service.DocumentGenerationService
import com.ritense.valueresolver.ValueResolverService
import com.ritense.zakenapi.ZaakUrlProvider
import com.ritense.zakenapi.client.ZakenApiClient
-import com.rotterdam.xential.api.DefaultApi
import org.camunda.bpm.engine.RuntimeService
-import org.openapitools.client.infrastructure.ApiClient
-import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.annotation.Bean
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
-import org.springframework.web.reactive.function.client.WebClient
@AutoConfiguration
@EnableJpaRepositories(basePackages = ["com.ritense.valtimo.xential.repository"])
@@ -60,23 +57,9 @@ class XentialAutoConfiguration {
return LiquibaseMasterChangeLogLocation("config/liquibase/xential-plugin-master.xml")
}
- @Bean
- @ConditionalOnMissingBean
- fun defaultApi(): DefaultApi {
- return DefaultApi()
- }
-
-// @Value("\${plugin.xential.baseurl: }") baseUrl: String
-
- @Bean
- @ConditionalOnMissingBean
- fun apiClient(
- ) = ApiClient("http://localhost:1080")
-
@Bean
@ConditionalOnMissingBean
fun documentGenerationService(
- defaultApi: DefaultApi,
xentialTokenRepository: XentialTokenRepository,
pluginService: PluginService,
documentenApiClient: DocumentenApiClient,
@@ -84,9 +67,9 @@ class XentialAutoConfiguration {
zaakUrlProvider: ZaakUrlProvider,
zakenApiClient: ZakenApiClient,
runtimeService: RuntimeService,
- valueResolverService: ValueResolverService
+ valueResolverService: ValueResolverService,
+ userManagementService: UserManagementService
) = DocumentGenerationService(
- defaultApi,
xentialTokenRepository,
pluginService,
documentenApiClient,
@@ -94,7 +77,7 @@ class XentialAutoConfiguration {
zaakUrlProvider,
zakenApiClient,
runtimeService,
- valueResolverService
+ valueResolverService,
+ userManagementService
)
-
}
diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/HttpClientProperties.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/HttpClientProperties.kt
new file mode 100644
index 0000000..17f8388
--- /dev/null
+++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/HttpClientProperties.kt
@@ -0,0 +1,13 @@
+package com.ritense.valtimo.xential.domain
+
+import java.io.File
+import java.net.URI
+
+data class HttpClientProperties(
+ val applicationName: String,
+ val applicationPassword: String,
+ val baseUrl: URI,
+ val serverCertificateFilename: File,
+ val clientPrivateKeyFilename: File?,
+ val clientCertFile: File?
+)
diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt
index dd9eabd..c050c87 100644
--- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt
+++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt
@@ -2,21 +2,41 @@ package com.ritense.valtimo.xential.domain
import jakarta.persistence.Column
import jakarta.persistence.Entity
+import jakarta.persistence.GeneratedValue
+import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
-import java.net.URI
import java.util.UUID
@Entity
@Table(name = "xential_tokens")
data class XentialToken (
+
@Id
- @Column(name = "token", nullable = false, updatable = false)
- val token: UUID,
- @Column(name = "process_id", nullable = false, updatable = false)
- val processId: UUID,
- @Column(name = "message_name", nullable = false, updatable = false)
- val messageName: String,
- @Column(name = "resume_url", nullable = true, updatable = false)
- val resumeUrl: URI?,
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "token")
+ val token: UUID = UUID.randomUUID(),
+
+ @Column(name = "external_token")
+ val externalToken: String = "",
+
+ @Column(name = "process_id")
+ val processId: UUID = UUID.randomUUID(),
+
+ @Column(name = "message_name")
+ val messageName: String = "",
+
+ @Column(name = "resume_url")
+ val resumeUrl: String = ""
+
+// @Id
+// @Column(name = "token", nullable = false, updatable = false)
+// val token: UUID,
+// @Column(name = "process_id", nullable = false, updatable = false)
+// val processId: UUID,
+// @Column(name = "message_name", nullable = false, updatable = false)
+// val messageName: String,
+// @Column(name = "resume_url", nullable = true, updatable = false)
+// val resumeUrl: URI?, @Id
+// @Column(name = "token", nullable = false, updatable = false)
)
diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt
index 5b34cb2..0f9da86 100644
--- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt
+++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt
@@ -22,28 +22,35 @@ import com.ritense.plugin.annotation.PluginAction
import com.ritense.plugin.annotation.PluginActionProperty
import com.ritense.plugin.annotation.PluginProperty
import com.ritense.processlink.domain.ActivityTypeWithEventName
+import com.ritense.valtimo.xential.domain.HttpClientProperties
import com.ritense.valtimo.xential.domain.FileFormat
import com.ritense.valtimo.xential.domain.GenerateDocumentProperties
import com.ritense.valtimo.xential.plugin.XentialPlugin.Companion.PLUGIN_KEY
import com.ritense.valtimo.xential.service.DocumentGenerationService
import com.ritense.zakenapi.ZakenApiPlugin
import org.camunda.bpm.engine.delegate.DelegateExecution
+import java.io.File
+import java.net.URI
import java.util.UUID
@Plugin(
key = PLUGIN_KEY,
title = "Xential Plugin",
- description = ""
+ description = "handle xentail requests"
)
+@Suppress("UNUSED")
class XentialPlugin(
- val documentGenerationService: DocumentGenerationService
+ private val documentGenerationService: DocumentGenerationService
) {
- @PluginProperty(key = "clientId", secret = false)
- private lateinit var clientId: String
+ @PluginProperty(key = "applicationName", secret = false, required = true)
+ private lateinit var applicationName: String
- @PluginProperty(key = "clientPassword", secret = true)
- private lateinit var clientPassword: String
+ @PluginProperty(key = "applicationPassword", secret = true, required = true)
+ private lateinit var applicationPassword: String
+
+ @PluginProperty(key = "baseUrl", secret = false, required = true)
+ lateinit var baseUrl: URI
@PluginProperty(key = "documentenApiPluginConfiguration", secret = false)
lateinit var documentenApiPluginConfiguration: DocumentenApiPlugin
@@ -51,6 +58,14 @@ class XentialPlugin(
@PluginProperty(key = "zakenApiPluginConfiguration", secret = false)
lateinit var zakenApiPluginConfiguration: ZakenApiPlugin
+ @PluginProperty(key = "serverCertificateFilename", secret = false, required = true)
+ private lateinit var serverCertificateFilename: String
+
+ @PluginProperty(key = "clientPrivateKeyFilename", secret = false, required = false)
+ var clientPrivateKeyFilename: String? = null
+
+ @PluginProperty(key = "clientCertificateFilename", secret = false, required = false)
+ var clientCertificateFilename: String? = null
@PluginAction(
key = "generate-document",
@@ -73,11 +88,20 @@ class XentialPlugin(
messageName,
templateData
)
+
+ val httpClientProperties = HttpClientProperties(
+ applicationName,
+ applicationPassword,
+ baseUrl,
+ File(serverCertificateFilename),
+ clientPrivateKeyFilename?.let{File(it)},
+ clientCertificateFilename?.let {File(it)}
+ )
+
documentGenerationService.generateDocument(
+ httpClientProperties,
UUID.fromString(execution.processInstanceId),
generateDocumentProperties,
- clientId,
- clientPassword,
execution
)
}
diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt
index 505b3b3..7156ff3 100644
--- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt
+++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt
@@ -5,6 +5,8 @@ import com.ritense.documentenapi.client.DocumentStatusType
import com.ritense.documentenapi.client.DocumentenApiClient
import com.ritense.documentenapi.event.DocumentCreated
import com.ritense.plugin.service.PluginService
+import com.ritense.valtimo.contract.authentication.UserManagementService
+import com.ritense.valtimo.xential.domain.HttpClientProperties
import com.ritense.valtimo.xential.domain.DocumentCreatedMessage
import com.ritense.valtimo.xential.domain.GenerateDocumentProperties
import com.ritense.valtimo.xential.domain.XentialToken
@@ -17,42 +19,52 @@ import com.ritense.zakenapi.client.LinkDocumentRequest
import com.ritense.zakenapi.client.ZakenApiClient
import com.rotterdam.xential.api.DefaultApi
import com.rotterdam.xential.model.Sjabloondata
+import mu.KotlinLogging
+import okhttp3.Credentials
+import okhttp3.OkHttpClient
import org.camunda.bpm.engine.RuntimeService
import org.camunda.bpm.engine.delegate.DelegateExecution
-import org.openapitools.client.infrastructure.ApiClient
import org.springframework.context.ApplicationEventPublisher
import java.io.ByteArrayInputStream
+import java.io.File
+import java.io.FileInputStream
+import java.security.KeyFactory
+import java.security.KeyStore
+import java.security.cert.CertificateFactory
+import java.security.spec.PKCS8EncodedKeySpec
import java.time.LocalDate
-import java.util.Base64
-import java.util.UUID
+import java.util.*
+import javax.net.ssl.KeyManagerFactory
+import javax.net.ssl.SSLContext
+import javax.net.ssl.TrustManagerFactory
+import javax.net.ssl.X509TrustManager
+
class DocumentGenerationService(
- val defaultApi: DefaultApi,
- val xentialTokenRepository: XentialTokenRepository,
- val pluginService: PluginService,
- val documentenApiClient: DocumentenApiClient,
- val applicationEventPublisher: ApplicationEventPublisher,
- val zaakUrlProvider: ZaakUrlProvider,
- val zakenApiClient: ZakenApiClient,
- val runtimeService: RuntimeService,
- val valueResolverService: ValueResolverService
- ) {
+ private val xentialTokenRepository: XentialTokenRepository,
+ private val pluginService: PluginService,
+ private val documentenApiClient: DocumentenApiClient,
+ private val applicationEventPublisher: ApplicationEventPublisher,
+ private val zaakUrlProvider: ZaakUrlProvider,
+ private val zakenApiClient: ZakenApiClient,
+ private val runtimeService: RuntimeService,
+ private val valueResolverService: ValueResolverService,
+ private val userManagementService: UserManagementService
+) {
fun generateDocument(
+ httpClientProperties: HttpClientProperties,
processId: UUID,
generateDocumentProperties: GenerateDocumentProperties,
- clientId: String,
- clientPassword: String,
execution: DelegateExecution,
) {
+ logger.info { "current userid: ${userManagementService.currentUserId}" }
- val resolvedMap = resolveTemplateData(generateDocumentProperties.templateData,execution)
+ val api = configureClient(httpClientProperties)
+ val resolvedMap = resolveTemplateData(generateDocumentProperties.templateData, execution)
val sjabloonVulData = resolvedMap.map { "<${it.key}>${it.value}${it.key}>" }.joinToString()
-
- ApiClient.username = clientId
- ApiClient.password = clientPassword
- val result = defaultApi.creeerDocument(
- gebruikersId = clientId,
+ val result = api.creeerDocument(
+ gebruikersId = userManagementService.currentUserId,
accepteerOnbekend = false,
sjabloondata = Sjabloondata(
sjabloonId = generateDocumentProperties.templateId.toString(),
@@ -61,15 +73,89 @@ class DocumentGenerationService(
sjabloonVulData = "$sjabloonVulData"
)
)
-
+ logger.info { "found something: $result" }
val xentialToken = XentialToken(
token = UUID.fromString(result.documentCreatieSessieId),
+ externalToken = result.documentCreatieSessieId,
processId = processId,
messageName = generateDocumentProperties.messageName,
- resumeUrl = result.resumeUrl
+ resumeUrl = result.resumeUrl.toString()
)
-
+ logger.info { "token: ${xentialToken.token}" }
xentialTokenRepository.save(xentialToken)
+ logger.info { "ready" }
+ }
+
+ private fun trustManagerFactory(certFile: File): TrustManagerFactory {
+
+ // Load the server certificate
+ val certificateFactory = CertificateFactory.getInstance("X.509")
+ val serverCert = certificateFactory.generateCertificate(FileInputStream(certFile))
+
+ // Create a KeyStore with the server certificate
+ val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
+ load(null, null)
+ setCertificateEntry("server", serverCert)
+ }
+
+ // Configure the TrustManager
+ val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+ trustManagerFactory.init(trustStore)
+ return trustManagerFactory
+ }
+
+ private fun keyManagerFactory(privateKeyFile: File?, clientCertFile: File?): KeyManagerFactory? {
+ return if (privateKeyFile != null && clientCertFile != null) {
+ val certificateFactory = CertificateFactory.getInstance("X.509")
+ val clientCert = certificateFactory.generateCertificate(FileInputStream(clientCertFile))
+ val privateKey = loadPrivateKey(privateKeyFile)
+
+ // Create a KeyStore with the client certificate and private key
+ val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
+ load(null, null)
+ setKeyEntry("client", privateKey, null, arrayOf(clientCert))
+ }
+
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()).apply {
+ init(keyStore, null)
+ }
+ } else null
+ }
+
+ private fun configureClient(properties: HttpClientProperties): DefaultApi {
+ val trustManagerFactory = trustManagerFactory(properties.serverCertificateFilename)
+ val keyManagerFactory = keyManagerFactory(
+ properties.clientPrivateKeyFilename,
+ properties.clientCertFile
+ )
+
+ val sslContext = SSLContext.getInstance("TLS").apply {
+ init(keyManagerFactory?.keyManagers, trustManagerFactory.trustManagers, null)
+ }
+
+ val credentials = Credentials.basic(properties.applicationName, properties.applicationPassword)
+ val customClient = OkHttpClient.Builder()
+ .addInterceptor { chain ->
+ val request = chain.request().newBuilder()
+ .header("Authorization", credentials)
+ .build()
+ chain.proceed(request)
+ }
+ .sslSocketFactory(sslContext.socketFactory, trustManagerFactory.trustManagers[0] as X509TrustManager)
+ .build()
+
+ return DefaultApi(properties.baseUrl.toString(), customClient)
+ }
+
+ private fun loadPrivateKey(file: File): java.security.PrivateKey {
+ val keyBytes = file.readText()
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replace("-----END PRIVATE KEY-----", "")
+ .replace("\\s".toRegex(), "")
+ .let { Base64.getDecoder().decode(it) }
+
+ val keySpec = PKCS8EncodedKeySpec(keyBytes)
+ return KeyFactory.getInstance("RSA").generatePrivate(keySpec)
}
fun onDocumentGenerated(message: DocumentCreatedMessage) {
@@ -147,8 +233,12 @@ class DocumentGenerationService(
private fun getXentialPlugin(message: DocumentCreatedMessage): XentialPlugin {
//FIXME needs a way of determining the right plugin
- val pluginConfig = pluginService.findPluginConfiguration(XentialPlugin.PLUGIN_KEY) { _ -> true}
+ val pluginConfig = pluginService.findPluginConfiguration(XentialPlugin.PLUGIN_KEY) { _ -> true }
?: throw NoSuchElementException("Could not find Xential plugin")
return pluginService.createInstance(pluginConfig) as XentialPlugin
}
+
+ companion object {
+ private val logger = KotlinLogging.logger {}
+ }
}
diff --git a/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml b/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml
index 1c6e952..246dbca 100644
--- a/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml
+++ b/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml
@@ -19,12 +19,16 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/backend/plugin/src/main/resources/config/plugin/common.pluginconfig.json b/backend/plugin/src/main/resources/config/plugin/common.pluginconfig.json
index 2d3d8cf..a889e59 100644
--- a/backend/plugin/src/main/resources/config/plugin/common.pluginconfig.json
+++ b/backend/plugin/src/main/resources/config/plugin/common.pluginconfig.json
@@ -3,10 +3,14 @@
"id": "6e034748-8ed5-43de-a5f2-f80e1a4b60dd",
"title": "Xential",
"properties": {
- "clientId": "test",
- "clientPassword": "pwd",
+ "applicationName": "${ROTTERDAM_XENTIAL_ACCOUNT_NAME}",
+ "applicationPassword": "${ROTTERDAM_XENTIAL_ACCOUNT_PASSWORD}",
+ "baseUrl": "http://localhost:1080",
"documentenApiPluginConfiguration": "5474fe57-532a-4050-8d89-32e62ca3e895",
- "zakenApiPluginConfiguration": "3079d6fe-42e3-4f8f-a9db-52ce2507b7ee"
+ "zakenApiPluginConfiguration": "3079d6fe-42e3-4f8f-a9db-52ce2507b7ee",
+ "serverCertificateFilename": "${ROTTERDAM_ESB_SERVER_CERTIFICATE_FILENAME}",
+ "clientPrivateKeyFilename": "${ROTTERDAM_ESB_CLIENT_PRIVATE_KEY_FILENAME}",
+ "clientCertificateFilename": "${ROTTERDAM_ESB_CLIENT_CERTIFICATE_FILENAME}"
},
"pluginDefinitionKey": "xential"
}
diff --git a/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html b/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html
index 2a4c57c..4a8b39d 100644
--- a/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html
+++ b/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html
@@ -31,18 +31,64 @@
>
+ name="applicationName"
+ [title]="'applicationName' | pluginTranslate: pluginId | async"
+ [margin]="true"
+ [fullWidth]="true"
+ [defaultValue]="obs.prefill?.applicationName"
+ [disabled]="obs.disabled"
+ [required]="true"
+ >
+
+
+
+ name="serverCertificateFilename"
+ [title]="'serverCertificateFilename' | pluginTranslate: pluginId | async"
+ [margin]="true"
+ [fullWidth]="true"
+ [defaultValue]="obs.prefill?.serverCertificateFilename"
+ [disabled]="obs.disabled"
+ [required]="true"
+ >
+
+
+
+
;
-}
-
export {XentialConfig};
diff --git a/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts b/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts
index 9e4dd7f..358330e 100644
--- a/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts
+++ b/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts
@@ -2,6 +2,7 @@ interface GenerateDocumentConfig {
templateId: string;
fileFormat: FileFormat;
documentId: string;
+ messageName: "sfsefef"
templateData: Array<{key: string; value: string}>
}