diff --git a/.gitignore b/.gitignore
index 78eb422..f328e6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,4 @@ bin/
### OpenAPI ###
legalconsenthub/.api-client
+legalconsenthub/.api-client-middleware
diff --git a/.run/local-middleware-dummy.run.xml b/.run/local-middleware-dummy.run.xml
new file mode 100644
index 0000000..e7605cf
--- /dev/null
+++ b/.run/local-middleware-dummy.run.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.run/local-middleware.run.xml b/.run/local-middleware.run.xml
new file mode 100644
index 0000000..c63a2f3
--- /dev/null
+++ b/.run/local-middleware.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/local-h2.run.xml b/.run/local-server-backend-h2.run.xml
similarity index 77%
rename from .run/local-h2.run.xml
rename to .run/local-server-backend-h2.run.xml
index 672ebab..0a60a7a 100644
--- a/.run/local-h2.run.xml
+++ b/.run/local-server-backend-h2.run.xml
@@ -1,5 +1,5 @@
-
+
@@ -9,4 +9,4 @@
-
+
\ No newline at end of file
diff --git a/legalconsenthub-middleware/DUMMY_MODE.md b/legalconsenthub-middleware/DUMMY_MODE.md
new file mode 100644
index 0000000..c273c9e
--- /dev/null
+++ b/legalconsenthub-middleware/DUMMY_MODE.md
@@ -0,0 +1,125 @@
+# Dummy Mode Configuration
+
+This document explains how to use the dummy/mock mode in the legalconsenthub-middleware application.
+
+## Overview
+
+Dummy mode allows you to test the middleware API endpoints without requiring a physical smart card connected to your system. Instead of interacting with real smart card hardware via OpenSC, the application returns predefined dummy data.
+
+## How to Enable Dummy Mode
+
+### Option 1: Using Spring Profiles
+
+Start the application with the `dummy` profile:
+
+```bash
+# Using Gradle
+./gradlew bootRun --args='--spring.profiles.active=dummy'
+
+# Using JAR
+java -jar build/libs/legalconsenthub-middleware-*.jar --spring.profiles.active=dummy
+
+# Using environment variable
+export SPRING_PROFILES_ACTIVE=dummy
+./gradlew bootRun
+```
+
+### Option 2: Using IntelliJ IDEA Run Configuration
+
+A pre-configured run configuration named `local-middleware-dummy` is available in the `.run` directory. Simply:
+
+1. Open the project in IntelliJ IDEA
+2. Select "local-middleware-dummy" from the run configurations dropdown
+3. Click the run button
+
+### Option 3: Manual Configuration
+
+You can also manually set the dummy mode property:
+
+```bash
+./gradlew bootRun --args='--dummy.mode.enabled=true'
+```
+
+## What Gets Mocked
+
+### Smart Card Information
+- **Smart Card Info**: Always returns a successful response with dummy smart card information
+- **Certificates**: Always returns a successful response with two dummy certificates
+
+### Signature Operations
+- **Sign PDF Hash**: Always returns a successful dummy signature
+- **Verify Signature**: Always returns a successful verification result
+
+## Dummy Data
+
+### Smart Card Info
+```json
+{
+ "isPresent": true,
+ "label": "DUMMY Smart Card",
+ "serialNumber": "12345678",
+ "manufacturer": "Dummy Corp",
+ "model": "DummyCard 2024"
+}
+```
+
+### Available Certificates
+1. **Certificate 1**:
+ - ID: `01`
+ - Subject: `CN=John Doe, O=Example Company, L=Berlin, C=DE`
+ - Issuer: `CN=Dummy CA, O=Dummy Corp, C=DE`
+
+2. **Certificate 2**:
+ - ID: `02`
+ - Subject: `CN=Jane Smith, O=Test Organization, L=Munich, C=DE`
+ - Issuer: `CN=Test CA, O=Test Corp, C=DE`
+
+## Testing Signature Verification
+
+The dummy implementation always returns successful responses:
+
+- **All signatures**: Any signature verification request will return `isValid: true`
+- **Consistent behavior**: All requests return successful responses for predictable testing
+- **No validation**: The dummy mode doesn't perform actual signature validation
+
+## API Endpoints
+
+All original API endpoints remain the same when running in dummy mode:
+
+- `GET /smart-card/info` - Returns dummy smart card information
+- `GET /smart-card/certificates` - Returns dummy certificates
+- `POST /sign-pdf-hash` - Creates dummy signatures
+- `POST /verify-signature` - Verifies dummy signatures
+
+## Development Benefits
+
+Using dummy mode provides several advantages during development:
+
+1. **No Hardware Dependency**: Test frontend functionality without smart card hardware
+2. **Consistent Data**: Predictable responses make testing easier
+3. **Fast Development**: No waiting for smart card operations or PIN entry
+4. **Error Testing**: Easily test error scenarios by using invalid certificate IDs
+5. **CI/CD Integration**: Run automated tests without smart card hardware
+
+## Switching Back to Real Mode
+
+To disable dummy mode and use real smart card operations:
+
+1. Remove the `dummy` profile from `SPRING_PROFILES_ACTIVE`
+2. Ensure your smart card is connected and OpenSC is properly configured
+3. Start the application normally
+
+```bash
+# Normal mode (default)
+./gradlew bootRun
+
+# Or explicitly disable dummy mode
+./gradlew bootRun --args='--dummy.mode.enabled=false'
+```
+
+## Notes
+
+- In dummy mode, all responses are successful and verification details are prefixed with "DUMMY:" to clearly indicate mock responses
+- The dummy controllers return hardcoded successful responses without any actual processing
+- All dummy responses include realistic data structures that match the real API responses
+- Perfect for frontend testing where you need predictable successful responses
\ No newline at end of file
diff --git a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/DummySignatureController.kt b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/DummySignatureController.kt
new file mode 100644
index 0000000..2df4caf
--- /dev/null
+++ b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/DummySignatureController.kt
@@ -0,0 +1,53 @@
+package com.betriebsratkanzlei.legalconsenthub_middleware.signature
+
+import com.betriebsratkanzlei.legalconsenthub_middleware_api.api.SignatureApi
+import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.VerifySignatureResponseDto
+import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.CertificateDto
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.multipart.MultipartFile
+import java.time.LocalDateTime
+import java.util.*
+
+@RestController
+@ConditionalOnProperty(name = ["dummy.mode.enabled"], havingValue = "true")
+class DummySignatureController : SignatureApi {
+
+ override fun signPdfHash(
+ document: MultipartFile,
+ certificateId: String,
+ hashAlgorithm: String
+ ): ResponseEntity {
+ // Always return a successful dummy signature
+ val dummySignature = "dummySignature_${certificateId}_${System.currentTimeMillis()}"
+ val encodedSignature = Base64.getEncoder().encodeToString(dummySignature.toByteArray())
+ return ResponseEntity.ok(encodedSignature)
+ }
+
+ override fun verifySignature(
+ document: MultipartFile,
+ signature: String,
+ certificateId: String?,
+ hashAlgorithm: String
+ ): ResponseEntity {
+ // Always return successful verification with dummy certificate
+ val dummyCertificate = CertificateDto(
+ id = certificateId ?: "01",
+ subject = "CN=John Doe, O=Example Company, L=Berlin, C=DE",
+ issuer = "CN=Dummy CA, O=Dummy Corp, C=DE",
+ validFrom = LocalDateTime.now().minusYears(1),
+ validTo = LocalDateTime.now().plusYears(2),
+ keyUsage = listOf("digitalSignature", "keyEncipherment", "nonRepudiation"),
+ fingerprint = "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD"
+ )
+
+ val successfulResponse = VerifySignatureResponseDto(
+ isValid = true,
+ certificateInfo = dummyCertificate,
+ verificationDetails = "DUMMY: Signature verified successfully using ${hashAlgorithm} algorithm"
+ )
+
+ return ResponseEntity.ok(successfulResponse)
+ }
+}
\ No newline at end of file
diff --git a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureController.kt b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureController.kt
index 3fc62dc..a278dd4 100644
--- a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureController.kt
+++ b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureController.kt
@@ -2,11 +2,13 @@ package com.betriebsratkanzlei.legalconsenthub_middleware.signature
import com.betriebsratkanzlei.legalconsenthub_middleware_api.api.SignatureApi
import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.VerifySignatureResponseDto
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
@RestController
+@ConditionalOnProperty(name = ["dummy.mode.enabled"], havingValue = "false", matchIfMissing = true)
class SignatureController(
private val signatureService: SignatureService
) : SignatureApi {
diff --git a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureService.kt b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureService.kt
index bac06c6..ef8a051 100644
--- a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureService.kt
+++ b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/signature/SignatureService.kt
@@ -3,10 +3,12 @@ package com.betriebsratkanzlei.legalconsenthub_middleware.signature
import com.betriebsratkanzlei.legalconsenthub_middleware.smartcard.SmartCardService
import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.VerifySignatureResponseDto
import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.CertificateDto
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Service
import java.util.*
@Service
+@ConditionalOnProperty(name = ["dummy.mode.enabled"], havingValue = "false", matchIfMissing = true)
class SignatureService(
private val smartCardService: SmartCardService
) {
diff --git a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/DummySmartCardController.kt b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/DummySmartCardController.kt
new file mode 100644
index 0000000..c5e7078
--- /dev/null
+++ b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/DummySmartCardController.kt
@@ -0,0 +1,51 @@
+package com.betriebsratkanzlei.legalconsenthub_middleware.smartcard
+
+import com.betriebsratkanzlei.legalconsenthub_middleware_api.api.SmartCardApi
+import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.CertificateDto
+import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.SmartCardInfoDto
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.RestController
+import java.time.LocalDateTime
+
+@RestController
+@ConditionalOnProperty(name = ["dummy.mode.enabled"], havingValue = "true")
+class DummySmartCardController : SmartCardApi {
+
+ override fun getSmartCardInfo(): ResponseEntity {
+ // Always return successful smart card info
+ val dummySmartCardInfo = SmartCardInfoDto(
+ isPresent = true,
+ label = "DUMMY Smart Card",
+ serialNumber = "12345678",
+ manufacturer = "Dummy Corp",
+ model = "DummyCard 2024"
+ )
+ return ResponseEntity.ok(dummySmartCardInfo)
+ }
+
+ override fun getSmartCardCertificates(): ResponseEntity> {
+ // Always return successful list of dummy certificates
+ val dummyCertificates = listOf(
+ CertificateDto(
+ id = "01",
+ subject = "CN=John Doe, O=Example Company, L=Berlin, C=DE",
+ issuer = "CN=Dummy CA, O=Dummy Corp, C=DE",
+ validFrom = LocalDateTime.now().minusYears(1),
+ validTo = LocalDateTime.now().plusYears(2),
+ keyUsage = listOf("digitalSignature", "keyEncipherment", "nonRepudiation"),
+ fingerprint = "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD"
+ ),
+ CertificateDto(
+ id = "02",
+ subject = "CN=Jane Smith, O=Test Organization, L=Munich, C=DE",
+ issuer = "CN=Test CA, O=Test Corp, C=DE",
+ validFrom = LocalDateTime.now().minusMonths(6),
+ validTo = LocalDateTime.now().plusYears(3),
+ keyUsage = listOf("digitalSignature", "nonRepudiation"),
+ fingerprint = "11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44"
+ )
+ )
+ return ResponseEntity.ok(dummyCertificates)
+ }
+}
\ No newline at end of file
diff --git a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardController.kt b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardController.kt
index fbcf9db..5cc4c50 100644
--- a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardController.kt
+++ b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardController.kt
@@ -3,11 +3,13 @@ package com.betriebsratkanzlei.legalconsenthub_middleware.smartcard
import com.betriebsratkanzlei.legalconsenthub_middleware_api.api.SmartCardApi
import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.CertificateDto
import com.betriebsratkanzlei.legalconsenthub_middleware_api.model.SmartCardInfoDto
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime
@RestController
+@ConditionalOnProperty(name = ["dummy.mode.enabled"], havingValue = "false", matchIfMissing = true)
class SmartCardController(
private val smartCardService: SmartCardService
) : SmartCardApi {
diff --git a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardService.kt b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardService.kt
index d807dfa..2b4d23e 100644
--- a/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardService.kt
+++ b/legalconsenthub-middleware/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub_middleware/smartcard/SmartCardService.kt
@@ -7,6 +7,7 @@ import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.apache.commons.exec.PumpStreamHandler
import org.springframework.beans.factory.annotation.Value
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.core.io.ResourceLoader
import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream
@@ -17,6 +18,7 @@ import java.nio.file.Files
import java.nio.file.StandardCopyOption
@Service
+@ConditionalOnProperty(name = ["dummy.mode.enabled"], havingValue = "false", matchIfMissing = true)
class SmartCardService(
@Value("\${opensc.pkcs11.library.path}") private val openscPkcs11LibPath: String,
private val resourceLoader: ResourceLoader
diff --git a/legalconsenthub-middleware/src/main/resources/application-dummy.yaml b/legalconsenthub-middleware/src/main/resources/application-dummy.yaml
new file mode 100644
index 0000000..d5b14ad
--- /dev/null
+++ b/legalconsenthub-middleware/src/main/resources/application-dummy.yaml
@@ -0,0 +1,28 @@
+spring:
+ application:
+ name: legalconsenthub-middleware
+ servlet:
+ multipart:
+ max-file-size: 10MB
+ max-request-size: 10MB
+
+server:
+ port: 8081
+ servlet:
+ context-path: /
+
+logging:
+ level:
+ com.betriebsratkanzlei.legalconsenthub_middleware: DEBUG
+ org.springframework.security: DEBUG
+
+# Dummy mode configuration
+dummy:
+ mode:
+ enabled: true
+
+# OpenSC configuration (not used in dummy mode but kept for consistency)
+opensc:
+ pkcs11:
+ library:
+ path: classpath:binaries/opensc-pkcs11.so
\ No newline at end of file
diff --git a/legalconsenthub/composables/middleware/useMiddleware.ts b/legalconsenthub/composables/middleware/useMiddleware.ts
new file mode 100644
index 0000000..d096012
--- /dev/null
+++ b/legalconsenthub/composables/middleware/useMiddleware.ts
@@ -0,0 +1,38 @@
+import type {
+ VerifySignatureHashAlgorithmEnum,
+ VerifySignatureResponseDto,
+ SignPdfHashHashAlgorithmEnum
+} from '~/.api-client-middleware'
+import { useMiddlewareApi } from '~/composables/middleware/useMiddlewareApi'
+
+export function useMiddleware() {
+ const middlewareApi = useMiddlewareApi()
+
+ async function signPdfHash(document: Blob, certificateId: string, hashAlgorithm?: SignPdfHashHashAlgorithmEnum) {
+ try {
+ return middlewareApi.signPdfHash(document, certificateId, hashAlgorithm)
+ } catch (e: unknown) {
+ console.error('Failed signing PDF hash:', e)
+ return Promise.reject(e)
+ }
+ }
+
+ async function verifySignature(
+ document: Blob,
+ signature: string,
+ certificateId?: string,
+ hashAlgorithm?: VerifySignatureHashAlgorithmEnum
+ ): Promise {
+ try {
+ return await middlewareApi.verifySignature(document, signature, certificateId, hashAlgorithm)
+ } catch (e: unknown) {
+ console.error('Failed verifying signature:', e)
+ return Promise.reject(e)
+ }
+ }
+
+ return {
+ signPdfHash,
+ verifySignature
+ }
+}
diff --git a/legalconsenthub/composables/middleware/useMiddlewareApi.ts b/legalconsenthub/composables/middleware/useMiddlewareApi.ts
new file mode 100644
index 0000000..e6508d2
--- /dev/null
+++ b/legalconsenthub/composables/middleware/useMiddlewareApi.ts
@@ -0,0 +1,45 @@
+import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
+import {
+ SmartCardApi,
+ SignatureApi,
+ Configuration,
+ type VerifySignatureHashAlgorithmEnum,
+ type VerifySignatureResponseDto,
+ type SignPdfHashHashAlgorithmEnum
+} from '~/.api-client-middleware'
+
+export function useMiddlewareApi() {
+ const appBaseUrl = useRuntimeConfig().app.baseURL
+ const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
+ const { jwt } = useAuth()
+
+ const basePath = withoutTrailingSlash(
+ cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
+ )
+
+ const smartCardApiClient = new SmartCardApi(
+ new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
+ )
+
+ const signatureApiClient = new SignatureApi(
+ new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
+ )
+
+ async function signPdfHash(document: Blob, certificateId: string, hashAlgorithm?: SignPdfHashHashAlgorithmEnum) {
+ return signatureApiClient.signPdfHash({ document, certificateId, hashAlgorithm })
+ }
+
+ async function verifySignature(
+ document: Blob,
+ signature: string,
+ certificateId?: string,
+ hashAlgorithm?: VerifySignatureHashAlgorithmEnum
+ ): Promise {
+ return signatureApiClient.verifySignature({ document, signature, certificateId, hashAlgorithm })
+ }
+
+ return {
+ signPdfHash,
+ verifySignature
+ }
+}