From f2bb3df25befab290a191ed0aa20708b9d03686d Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 18 Feb 2025 12:35:53 +0100 Subject: [PATCH] fixes --- .docker/admin-server/Dockerfile | 2 +- build.gradle.kts | 2 +- .../server/admin/controllers/KeyController.kt | 7 ++-- .../sphereon/oid/fed/kms/local/LocalKms.kt | 5 +-- .../oid/fed/openapi/admin-server.yaml | 16 +++---- .../oid/fed/client/helpers/Helpers.kt | 6 +-- .../client/services/jwtService/JwtService.kt | 7 ++-- .../trustChainService/TrustChainService.kt | 12 ++---- .../oid/fed/client/types/ICryptoService.kt | 4 +- ...EntityConfigurationStatementServiceTest.kt | 4 +- .../TrustChainServiceTest.kt | 4 +- .../oid/fed/client/FederationClient.js.kt | 3 +- .../oid/fed/client/crypto/Crypto.js.kt | 7 ++-- .../oid/fed/client/crypto/Crypto.jvm.kt | 4 +- ...tityConfigurationStatementObjectBuilder.kt | 12 ++---- .../SubordinateStatementObjectBuilder.kt | 5 ++- .../EntityConfigurationStatementService.kt | 7 ++-- .../sphereon/oid/fed/services/JwkService.kt | 12 +++--- .../sphereon/oid/fed/services/KmsService.kt | 4 +- .../oid/fed/services/LocalKmsClient.kt | 3 +- .../oid/fed/services/SubordinateService.kt | 6 +-- .../services/config/AccountServiceConfig.kt | 2 +- .../oid/fed/services/mappers/JwkMapper.kt | 42 ++++++++++++++----- .../services/mappers/SubordinateJwkMapper.kt | 9 +--- .../AccountServiceTest.kt | 2 +- ...EntityConfigurationStatementServiceTest.kt | 9 ++-- 26 files changed, 105 insertions(+), 91 deletions(-) diff --git a/.docker/admin-server/Dockerfile b/.docker/admin-server/Dockerfile index bbcf1429..06a7df6b 100644 --- a/.docker/admin-server/Dockerfile +++ b/.docker/admin-server/Dockerfile @@ -15,7 +15,7 @@ RUN microdnf install curl WORKDIR /app COPY --from=builder /app/modules/admin-server/build/libs/admin-server-*.jar ./admin-server.jar -HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8080/status || exit 1 +HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8081/status || exit 1 # Create non-root user RUN useradd -r -u 1002 -g root admin-server diff --git a/build.gradle.kts b/build.gradle.kts index aaea9922..26f0a717 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -92,7 +92,7 @@ fun getNpmVersion(): String { allprojects { group = "com.sphereon.oid.fed" - version = "0.4.19-SNAPSHOT" + version = "0.4.20-SNAPSHOT" val npmVersion by extra { getNpmVersion() } // Common repository configuration for all projects diff --git a/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/KeyController.kt b/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/KeyController.kt index 6b035471..4aaf3c46 100644 --- a/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/KeyController.kt +++ b/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/KeyController.kt @@ -3,6 +3,7 @@ package com.sphereon.oid.fed.server.admin.controllers import com.sphereon.oid.fed.common.Constants import com.sphereon.oid.fed.openapi.models.Account import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.EntityJwk import com.sphereon.oid.fed.openapi.models.Jwk import com.sphereon.oid.fed.services.JwkService import jakarta.servlet.http.HttpServletRequest @@ -16,13 +17,13 @@ class KeyController( ) { @PostMapping @ResponseStatus(HttpStatus.CREATED) - fun create(request: HttpServletRequest): Jwk { + fun create(request: HttpServletRequest): EntityJwk { val account = request.getAttribute(Constants.ACCOUNT_ATTRIBUTE) as Account return jwkService.createKey(account) } @GetMapping - fun getKeys(request: HttpServletRequest): Array { + fun getKeys(request: HttpServletRequest): Array { val account = request.getAttribute(Constants.ACCOUNT_ATTRIBUTE) as Account return jwkService.getKeys(account) } @@ -32,7 +33,7 @@ class KeyController( request: HttpServletRequest, @PathVariable keyId: Int, @RequestParam reason: String? - ): Jwk { + ): EntityJwk { val account = request.getAttribute(Constants.ACCOUNT_ATTRIBUTE) as Account return jwkService.revokeKey(account, keyId, reason) } diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt index 28a2caa3..f51bdfb2 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt @@ -2,7 +2,6 @@ package com.sphereon.oid.fed.kms.local import com.sphereon.oid.fed.kms.local.database.LocalKmsDatabase import com.sphereon.oid.fed.kms.local.encryption.AesEncryption -import com.sphereon.oid.fed.kms.local.extensions.toJwk import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair import com.sphereon.oid.fed.kms.local.jwt.sign import com.sphereon.oid.fed.kms.local.jwt.verify @@ -17,7 +16,7 @@ class LocalKms { private val database: LocalKmsDatabase = LocalKmsDatabase() private val aesEncryption: AesEncryption = AesEncryption() - fun generateKey(): Jwk { + fun generateKey(): JwkWithPrivateKey { val jwk = generateKeyPair() database.insertKey( @@ -25,7 +24,7 @@ class LocalKms { key = aesEncryption.encrypt(Json.encodeToString(JwkWithPrivateKey.serializer(), jwk)) ) - return jwk.toJwk() + return jwk } fun sign(header: JwtHeader, payload: JsonObject, keyId: String): String { diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/admin-server.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/admin-server.yaml index b4b54574..af98e0a6 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/admin-server.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/admin-server.yaml @@ -682,7 +682,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BaseJwk' + $ref: '#/components/schemas/Jwk' responses: '201': description: Subordinate JWK created successfully @@ -1645,7 +1645,7 @@ components: - accountId - admin - BaseJwk: + Jwk: type: object x-tags: - federation @@ -1752,7 +1752,7 @@ components: keys: type: array items: - $ref: '#/components/schemas/BaseJwk' + $ref: '#/components/schemas/Jwk' metadata: additionalProperties: true crit: @@ -1996,7 +1996,7 @@ components: HistoricalKey: allOf: - - $ref: '#/components/schemas/BaseJwk' + - $ref: '#/components/schemas/Jwk' - type: object x-tags: - federation @@ -2014,9 +2014,9 @@ components: revoked: $ref: '#/components/schemas/JwkRevoked' - Jwk: + EntityJwk: allOf: - - $ref: '#/components/schemas/BaseJwk' + - $ref: '#/components/schemas/Jwk' - type: object x-tags: - federation @@ -2067,7 +2067,7 @@ components: JwkWithPrivateKey: allOf: - - $ref: '#/components/schemas/BaseJwk' + - $ref: '#/components/schemas/Jwk' - type: object properties: d: @@ -2496,7 +2496,7 @@ components: jwks: type: array items: - $ref: '#/components/schemas/BaseJwk' + $ref: '#/components/schemas/Jwk' additionalProperties: type: string diff --git a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/helpers/Helpers.kt b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/helpers/Helpers.kt index 01fd05cf..31381971 100644 --- a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/helpers/Helpers.kt +++ b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/helpers/Helpers.kt @@ -1,6 +1,6 @@ package com.sphereon.oid.fed.client.helpers -import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.datetime.Clock import kotlinx.serialization.json.Json @@ -12,13 +12,13 @@ fun getSubordinateStatementEndpoint(fetchEndpoint: String, sub: String): String return "${fetchEndpoint}?sub=$sub" } -fun findKeyInJwks(keys: Array, kid: String, json: Json): BaseJwk? { +fun findKeyInJwks(keys: Array, kid: String, json: Json): Jwk? { val key = keys.firstOrNull { it.kid?.trim() == kid.trim() } return key } -fun checkKidInJwks(keys: Array, kid: String): Boolean { +fun checkKidInJwks(keys: Array, kid: String): Boolean { for (key in keys) { if (key.kid == kid) { return true diff --git a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/jwtService/JwtService.kt b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/jwtService/JwtService.kt index b6e6b120..487b6d0c 100644 --- a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/jwtService/JwtService.kt +++ b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/jwtService/JwtService.kt @@ -3,12 +3,13 @@ package com.sphereon.oid.fed.client.services.jwtService import com.sphereon.oid.fed.client.context.FederationContext import com.sphereon.oid.fed.client.mapper.decodeJWTComponents import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.builtins.ArraySerializer import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject class JwtService(private val context: FederationContext) { - suspend fun fetchAndVerifyJwt(endpoint: String, verifyWithKey: BaseJwk? = null): String { + suspend fun fetchAndVerifyJwt(endpoint: String, verifyWithKey: Jwk? = null): String { context.logger.debug("Fetching JWT from endpoint: $endpoint") val jwt = context.httpResolver.get(endpoint) @@ -21,7 +22,7 @@ class JwtService(private val context: FederationContext) { return jwt } - suspend fun verifyJwt(jwt: String, key: BaseJwk) { + suspend fun verifyJwt(jwt: String, key: Jwk) { context.logger.debug("Verifying JWT signature with key: ${key.kid}") if (!context.cryptoService.verify(jwt, key)) { throw IllegalStateException("JWT signature verification failed") @@ -34,7 +35,7 @@ class JwtService(private val context: FederationContext) { context.logger.debug("Verifying self-signed JWT with kid: ${decodedJwt.header.kid}") val jwks = decodedJwt.payload["jwks"]?.jsonObject?.get("keys")?.jsonArray?.let { array -> - context.json.decodeFromJsonElement(ArraySerializer(BaseJwk.serializer()), array) + context.json.decodeFromJsonElement(ArraySerializer(Jwk.serializer()), array) } ?: throw IllegalStateException("No JWKS found in JWT payload") val key = jwks.find { it.kid == decodedJwt.header.kid } diff --git a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainService.kt b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainService.kt index eeb42bcd..668f259c 100644 --- a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainService.kt +++ b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainService.kt @@ -7,10 +7,7 @@ import com.sphereon.oid.fed.client.mapper.mapEntityStatement import com.sphereon.oid.fed.client.services.entityConfigurationStatementService.EntityConfigurationStatementService import com.sphereon.oid.fed.client.types.TrustChainResolveResponse import com.sphereon.oid.fed.client.types.VerifyTrustChainResponse -import com.sphereon.oid.fed.openapi.models.BaseJwk -import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement -import com.sphereon.oid.fed.openapi.models.Jwt -import com.sphereon.oid.fed.openapi.models.SubordinateStatement +import com.sphereon.oid.fed.openapi.models.* import kotlinx.serialization.builtins.ArraySerializer import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonArray @@ -252,9 +249,6 @@ class TrustChainService( } ?: run { logger.debug("No JWKS found in entity configuration payload") return null - } ?: run { - logger.debug("Could not find key with kid: ${decodedEntityConfiguration.header.kid} in JWKS") - return null } context.jwtService.verifyJwt(entityConfigurationJwt, key) @@ -505,8 +499,8 @@ class TrustChainService( return context.cryptoService.verify(jwt, key) } - private fun decodeJwksArray(jsonArray: kotlinx.serialization.json.JsonArray): Array { - return context.json.decodeFromJsonElement(ArraySerializer(BaseJwk.serializer()), jsonArray) + private fun decodeJwksArray(jsonArray: kotlinx.serialization.json.JsonArray): Array { + return context.json.decodeFromJsonElement(ArraySerializer(Jwk.serializer()), jsonArray) } } diff --git a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/types/ICryptoService.kt b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/types/ICryptoService.kt index a11cc49a..1d6d36eb 100644 --- a/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/types/ICryptoService.kt +++ b/modules/openid-federation-client/src/commonMain/kotlin/com/sphereon/oid/fed/client/types/ICryptoService.kt @@ -1,12 +1,12 @@ package com.sphereon.oid.fed.client.types -import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.Jwk import kotlin.js.JsExport @JsExport.Ignore interface ICryptoService { suspend fun verify( jwt: String, - key: BaseJwk + key: Jwk ): Boolean } diff --git a/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/entityConfigurationStatementService/EntityConfigurationStatementServiceTest.kt b/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/entityConfigurationStatementService/EntityConfigurationStatementServiceTest.kt index eeeac7d9..e988ffaa 100644 --- a/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/entityConfigurationStatementService/EntityConfigurationStatementServiceTest.kt +++ b/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/entityConfigurationStatementService/EntityConfigurationStatementServiceTest.kt @@ -6,7 +6,7 @@ import com.sphereon.oid.fed.client.mapper.InvalidJwtException import com.sphereon.oid.fed.client.mockResponses.mockResponses import com.sphereon.oid.fed.client.types.ICryptoService import com.sphereon.oid.fed.logger.Logger -import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.Jwk import io.ktor.client.* import io.ktor.client.engine.mock.* import io.ktor.client.plugins.* @@ -16,7 +16,7 @@ import kotlin.test.* import kotlin.time.Duration.Companion.seconds object TestCryptoService : ICryptoService { - override suspend fun verify(jwt: String, key: BaseJwk): Boolean { + override suspend fun verify(jwt: String, key: Jwk): Boolean { return true } } diff --git a/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainServiceTest.kt b/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainServiceTest.kt index 1c711f18..c6902942 100644 --- a/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainServiceTest.kt +++ b/modules/openid-federation-client/src/commonTest/kotlin/com/sphereon/oid/fed/client/services/trustChainService/TrustChainServiceTest.kt @@ -4,7 +4,7 @@ import com.sphereon.oid.fed.client.FederationClient import com.sphereon.oid.fed.client.mockResponses.mockResponses import com.sphereon.oid.fed.client.types.ICryptoService import com.sphereon.oid.fed.logger.Logger -import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.Jwk import io.ktor.client.* import io.ktor.client.engine.mock.* import io.ktor.client.plugins.* @@ -16,7 +16,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFalse object CryptoService : ICryptoService { - override suspend fun verify(jwt: String, key: BaseJwk): Boolean { + override suspend fun verify(jwt: String, key: Jwk): Boolean { return true } } diff --git a/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/FederationClient.js.kt b/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/FederationClient.js.kt index bcfef5f7..89a5b88e 100644 --- a/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/FederationClient.js.kt +++ b/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/FederationClient.js.kt @@ -11,6 +11,7 @@ import com.sphereon.oid.fed.client.types.TrustMarkValidationResponse import com.sphereon.oid.fed.client.types.VerifyTrustChainResponse import com.sphereon.oid.fed.openapi.models.BaseJwk import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement +import com.sphereon.oid.fed.openapi.models.Jwk import io.ktor.client.* import io.ktor.client.engine.js.* import io.ktor.client.plugins.* @@ -25,7 +26,7 @@ import kotlin.js.Promise external interface ICryptoServiceJS { fun verify( jwt: String, - key: BaseJwk + key: Jwk ): Promise } diff --git a/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt b/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt index eeb196be..0a72e5a4 100644 --- a/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt +++ b/modules/openid-federation-client/src/jsMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.js.kt @@ -3,7 +3,6 @@ package com.sphereon.oid.fed.client.crypto import ICryptoServiceJS import com.sphereon.oid.fed.client.mapper.decodeJWTComponents import com.sphereon.oid.fed.client.types.ICryptoService -import com.sphereon.oid.fed.openapi.models.BaseJwk import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.coroutines.await import kotlinx.serialization.encodeToString @@ -18,13 +17,13 @@ external object Jose { } class CryptoServiceAdapter(private val jsCryptoService: ICryptoServiceJS) : ICryptoService { - override suspend fun verify(jwt: String, key: BaseJwk): Boolean { + override suspend fun verify(jwt: String, key: Jwk): Boolean { return jsCryptoService.verify(jwt, key).await() } } object CryptoServiceJS : ICryptoServiceJS { - override fun verify(jwt: String, key: BaseJwk): Promise { + override fun verify(jwt: String, key: Jwk): Promise { return Promise { resolve, reject -> try { val decodedJwt = decodeJWTComponents(jwt) @@ -51,7 +50,7 @@ object CryptoServiceJS : ICryptoServiceJS { @JsExport.Ignore actual fun cryptoService(): ICryptoService { return object : ICryptoService { - override suspend fun verify(jwt: String, key: BaseJwk): Boolean { + override suspend fun verify(jwt: String, key: Jwk): Boolean { return CryptoServiceJS.verify(jwt, key).await() } } diff --git a/modules/openid-federation-client/src/jvmMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.jvm.kt b/modules/openid-federation-client/src/jvmMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.jvm.kt index ffa1afc6..5f7fab8a 100644 --- a/modules/openid-federation-client/src/jvmMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.jvm.kt +++ b/modules/openid-federation-client/src/jvmMain/kotlin/com/sphereon/oid/fed/client/crypto/Crypto.jvm.kt @@ -9,14 +9,14 @@ import com.nimbusds.jose.jwk.JWK import com.nimbusds.jose.jwk.KeyType import com.nimbusds.jwt.SignedJWT import com.sphereon.oid.fed.client.types.ICryptoService -import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.text.ParseException actual fun cryptoService(): ICryptoService { return object : ICryptoService { - override suspend fun verify(jwt: String, key: BaseJwk): Boolean { + override suspend fun verify(jwt: String, key: Jwk): Boolean { return try { val signedJWT = SignedJWT.parse(jwt) diff --git a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementObjectBuilder.kt b/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementObjectBuilder.kt index 620bec27..a3398baf 100644 --- a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementObjectBuilder.kt +++ b/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementObjectBuilder.kt @@ -1,16 +1,13 @@ package com.sphereon.oid.fed.common.builder -import com.sphereon.oid.fed.openapi.models.BaseJwk -import com.sphereon.oid.fed.openapi.models.BaseStatementJwks -import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement -import com.sphereon.oid.fed.openapi.models.TrustMark +import com.sphereon.oid.fed.openapi.models.* import kotlinx.serialization.json.JsonObject class EntityConfigurationStatementObjectBuilder { private var iss: String? = null private var exp: Int? = null private var iat: Int? = null - private lateinit var jwks: List + private lateinit var jwks: List private var metadata: MutableMap = mutableMapOf() private val authorityHints: MutableList = mutableListOf() private val trustMarkIssuers: MutableMap> = mutableMapOf() @@ -20,7 +17,7 @@ class EntityConfigurationStatementObjectBuilder { fun iss(iss: String) = apply { this.iss = iss } fun exp(exp: Int) = apply { this.exp = exp } fun iat(iat: Int) = apply { this.iat = iat } - fun jwks(jwks: List) = apply { this.jwks = jwks } + fun jwks(jwks: List) = apply { this.jwks = jwks } fun metadata(metadata: Pair) = apply { @@ -43,7 +40,7 @@ class EntityConfigurationStatementObjectBuilder { this.trustMarks.add(trustMark) } - private fun createJwks(jwks: List): BaseStatementJwks { + private fun createJwks(jwks: List): BaseStatementJwks { return BaseStatementJwks(jwks.toTypedArray()) } @@ -59,7 +56,6 @@ class EntityConfigurationStatementObjectBuilder { crit = if (crit.isNotEmpty()) crit.toTypedArray() else null, trustMarkIssuers = this.trustMarkIssuers.map { (k, v) -> k to v.toTypedArray() }.toMap(), trustMarks = trustMarks.toTypedArray() - ) } } diff --git a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/SubordinateStatementObjectBuilder.kt b/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/SubordinateStatementObjectBuilder.kt index fc77aa69..10c61e1b 100644 --- a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/SubordinateStatementObjectBuilder.kt +++ b/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/SubordinateStatementObjectBuilder.kt @@ -2,6 +2,7 @@ package com.sphereon.oid.fed.common.builder import com.sphereon.oid.fed.openapi.models.BaseJwk import com.sphereon.oid.fed.openapi.models.BaseStatementJwks +import com.sphereon.oid.fed.openapi.models.Jwk import com.sphereon.oid.fed.openapi.models.SubordinateStatement import kotlinx.serialization.json.JsonObject @@ -10,7 +11,7 @@ class SubordinateStatementObjectBuilder { private var sub: String? = null private var exp: Int? = null private var iat: Int? = null - private var jwks: MutableList = mutableListOf() + private var jwks: MutableList = mutableListOf() private var metadata: MutableMap = mutableMapOf() private var metadata_policy: MutableMap = mutableMapOf() private var metadata_policy_crit: MutableMap = mutableMapOf() @@ -38,7 +39,7 @@ class SubordinateStatementObjectBuilder { this.crit.add(claim) } - fun jwks(jwk: BaseJwk) = apply { + fun jwks(jwk: Jwk) = apply { this.jwks.add(jwk) } diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationStatementService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationStatementService.kt index b992b7c3..b62010dd 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationStatementService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationStatementService.kt @@ -5,10 +5,11 @@ import com.sphereon.oid.fed.common.builder.EntityConfigurationStatementObjectBui import com.sphereon.oid.fed.common.builder.FederationEntityMetadataObjectBuilder import com.sphereon.oid.fed.logger.Logger import com.sphereon.oid.fed.openapi.models.Account -import com.sphereon.oid.fed.openapi.models.BaseJwk import com.sphereon.oid.fed.openapi.models.FederationEntityMetadata +import com.sphereon.oid.fed.openapi.models.Jwk import com.sphereon.oid.fed.openapi.models.JwtHeader import com.sphereon.oid.fed.persistence.Persistence +import com.sphereon.oid.fed.services.mappers.toJwk import com.sphereon.oid.fed.services.mappers.toTrustMark import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject @@ -27,7 +28,7 @@ class EntityConfigurationStatementService( val identifier = accountService.getAccountIdentifierByAccount(account) val keys = jwkService.getKeys(account) - val entityConfigBuilder = createBaseEntityConfigurationStatement(identifier, keys) + val entityConfigBuilder = createBaseEntityConfigurationStatement(identifier, keys.map { it.toJwk() }.toTypedArray()) addOptionalMetadata(account, entityConfigBuilder, identifier) addAuthorityHints(account, entityConfigBuilder) @@ -42,7 +43,7 @@ class EntityConfigurationStatementService( private fun createBaseEntityConfigurationStatement( identifier: String, - keys: Array + keys: Array ): EntityConfigurationStatementObjectBuilder { return EntityConfigurationStatementObjectBuilder() .iss(identifier) diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/JwkService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/JwkService.kt index 9a3dd62e..c1c553cd 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/JwkService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/JwkService.kt @@ -3,9 +3,9 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.common.Constants import com.sphereon.oid.fed.common.exceptions.NotFoundException import com.sphereon.oid.fed.logger.Logger +import com.sphereon.oid.fed.openapi.models.Jwk import com.sphereon.oid.fed.openapi.models.* import com.sphereon.oid.fed.persistence.Persistence -import com.sphereon.oid.fed.services.mappers.toBaseJwk import com.sphereon.oid.fed.services.mappers.toDTO import com.sphereon.oid.fed.services.mappers.toHistoricalKey import kotlinx.serialization.json.Json @@ -17,7 +17,7 @@ class JwkService( private val logger = Logger.tag("KeyService") private val jwkQueries = Persistence.jwkQueries - fun createKey(account: Account): Jwk { + fun createKey(account: Account): EntityJwk { logger.info("Creating new key for account: ${account.username}") logger.debug("Found account with ID: ${account.id}") @@ -27,22 +27,22 @@ class JwkService( val createdJwk = jwkQueries.create( account_id = account.id, kid = jwk.kid, - key = Json.encodeToString(Jwk.serializer(), jwk), + key = Json.encodeToString(JwkWithPrivateKey.serializer(), jwk), ).executeAsOne() logger.info("Successfully created key with KID: ${jwk.kid} for account ID: ${account.id}") return createdJwk.toDTO() } - fun getKeys(account: Account): Array { + fun getKeys(account: Account): Array { logger.debug("Retrieving keys for account: ${account.username}") - val keys = jwkQueries.findByAccountId(account.id).executeAsList().map { it.toBaseJwk() }.toTypedArray() + val keys = jwkQueries.findByAccountId(account.id).executeAsList().map { it.toDTO() }.toTypedArray() logger.debug("Found ${keys.size} keys for account ID: ${account.id}") return keys } - fun revokeKey(account: Account, keyId: Int, reason: String?): Jwk { + fun revokeKey(account: Account, keyId: Int, reason: String?): EntityJwk { logger.info("Attempting to revoke key ID: $keyId for account: ${account.username}") logger.debug("Found account with ID: ${account.id}") diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KmsService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KmsService.kt index 7bd78962..517ba77d 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KmsService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KmsService.kt @@ -1,6 +1,8 @@ package com.sphereon.oid.fed.services +import com.sphereon.oid.fed.openapi.models.BaseJwk import com.sphereon.oid.fed.openapi.models.Jwk +import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey import com.sphereon.oid.fed.openapi.models.JwtHeader import kotlinx.serialization.json.JsonObject @@ -16,7 +18,7 @@ object KmsService { } interface KmsClient { - fun generateKeyPair(): Jwk + fun generateKeyPair(): JwkWithPrivateKey fun sign(header: JwtHeader, payload: JsonObject, keyId: String): String fun verify(token: String, jwk: Jwk): Boolean } diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt index ea789fcc..e7a33238 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt @@ -3,6 +3,7 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.kms.local.LocalKms import com.sphereon.oid.fed.openapi.models.Jwk +import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey import com.sphereon.oid.fed.openapi.models.JwtHeader import kotlinx.serialization.json.JsonObject @@ -10,7 +11,7 @@ class LocalKmsClient : KmsClient { private val localKms = LocalKms() - override fun generateKeyPair(): Jwk { + override fun generateKeyPair(): JwkWithPrivateKey { return localKms.generateKey() } diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/SubordinateService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/SubordinateService.kt index 95a9b6be..12b4009e 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/SubordinateService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/SubordinateService.kt @@ -8,8 +8,8 @@ import com.sphereon.oid.fed.logger.Logger import com.sphereon.oid.fed.openapi.models.* import com.sphereon.oid.fed.openapi.models.SubordinateMetadata import com.sphereon.oid.fed.persistence.Persistence -import com.sphereon.oid.fed.services.mappers.toBaseJwk import com.sphereon.oid.fed.services.mappers.toDTO +import com.sphereon.oid.fed.services.mappers.toJwk import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonObject @@ -93,7 +93,7 @@ class SubordinateService( logger.debug("Found subordinate with identifier: ${subordinate.identifier}") val subordinateJwks = - subordinateJwkQueries.findBySubordinateId(subordinate.id).executeAsList().map { it.toBaseJwk() } + subordinateJwkQueries.findBySubordinateId(subordinate.id).executeAsList().map { it.toJwk() } logger.debug("Found ${subordinateJwks.size} JWKs for subordinate") val subordinateMetadataList = @@ -113,7 +113,7 @@ class SubordinateService( private fun buildSubordinateStatement( account: Account, subordinate: SubordinateEntity, - subordinateJwks: List, + subordinateJwks: List, subordinateMetadataList: List ): SubordinateStatement { logger.debug("Building subordinate statement") diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/config/AccountServiceConfig.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/config/AccountServiceConfig.kt index 0d6193b3..1c370c7f 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/config/AccountServiceConfig.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/config/AccountServiceConfig.kt @@ -3,5 +3,5 @@ package com.sphereon.oid.fed.services.config /** * Configuration class for account-related settings. */ -class AccountServiceConfig(override val rootIdentifier: String = "http://localhost:8080") : IAccountServiceConfig { +class AccountServiceConfig(override val rootIdentifier: String) : IAccountServiceConfig { } \ No newline at end of file diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/JwkMapper.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/JwkMapper.kt index 88848dc1..e814483b 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/JwkMapper.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/JwkMapper.kt @@ -3,11 +3,12 @@ package com.sphereon.oid.fed.services.mappers import com.sphereon.oid.fed.openapi.models.* import kotlinx.serialization.json.Json import com.sphereon.oid.fed.persistence.models.Jwk as JwkEntity +import com.sphereon.oid.fed.openapi.models.EntityJwk -fun JwkEntity.toDTO(): Jwk { - val key = Json.decodeFromString(this.key) +fun JwkEntity.toDTO(): EntityJwk { + val key = Json.decodeFromString(this.key) - return Jwk( + return EntityJwk( id = this.id, e = key.e, x = key.x, @@ -27,10 +28,10 @@ fun JwkEntity.toDTO(): Jwk { ) } -fun JwkEntity.toHistoricalKey(): HistoricalKey { +fun JwkEntity.toJwk(): Jwk { val key = Json.decodeFromString(this.key) - return HistoricalKey( + return Jwk( e = key.e, x = key.x, y = key.y, @@ -43,15 +44,14 @@ fun JwkEntity.toHistoricalKey(): HistoricalKey { x5c = key.x5c, x5t = key.x5t, x5u = key.x5u, - x5tS256 = key.x5tS256, - revoked = key.revokedAt?.let { JwkRevoked(it, key.revokedReason) } + x5tS256 = key.x5tS256 ) } -fun JwkEntity.toBaseJwk(): BaseJwk { - val key = Json.decodeFromString(this.key) +fun JwkEntity.toHistoricalKey(): HistoricalKey { + val key = Json.decodeFromString(this.key) - return BaseJwk( + return HistoricalKey( e = key.e, x = key.x, y = key.y, @@ -65,5 +65,27 @@ fun JwkEntity.toBaseJwk(): BaseJwk { x5t = key.x5t, x5u = key.x5u, x5tS256 = key.x5tS256, + revoked = JwkRevoked( + reason = this.revoked_reason, + revokedAt = this.revoked_at.toString() + ) + ) +} + +fun EntityJwk.toJwk(): Jwk { + return Jwk( + e = this.e, + x = this.x, + y = this.y, + n = this.n, + alg = this.alg, + crv = this.crv, + kid = this.kid, + kty = this.kty, + use = this.use, + x5c = this.x5c, + x5t = this.x5t, + x5u = this.x5u, + x5tS256 = this.x5tS256 ) } \ No newline at end of file diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/SubordinateJwkMapper.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/SubordinateJwkMapper.kt index e76b455b..61c614ac 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/SubordinateJwkMapper.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/mappers/SubordinateJwkMapper.kt @@ -21,11 +21,6 @@ fun SubordinateJwkEntity.toDTO(): SubordinateJwk { ) } -fun SubordinateJwk.toJwk(): Jwk { - val jsonKey = key ?: throw IllegalArgumentException("SubordinateJwk.key cannot be null") - return json.decodeFromJsonElement(jsonKey) -} - -fun SubordinateJwkEntity.toBaseJwk(): BaseJwk { - return json.decodeFromString(this.key) +fun SubordinateJwkEntity.toJwk(): Jwk { + return json.decodeFromString(this.key) } \ No newline at end of file diff --git a/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/AccountServiceTest.kt b/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/AccountServiceTest.kt index 83cb8555..05d09cc2 100644 --- a/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/AccountServiceTest.kt +++ b/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/AccountServiceTest.kt @@ -24,7 +24,7 @@ class AccountServiceTest { @BeforeTest fun setup() { - config = AccountServiceConfig(Constants.DEFAULT_ROOT_USERNAME) + config = AccountServiceConfig() accountQueries = mockk(relaxed = true) mockkObject(Persistence) every { Persistence.accountQueries } returns accountQueries diff --git a/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/EntityConfigurationStatementServiceTest.kt b/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/EntityConfigurationStatementServiceTest.kt index 82474ebe..bcf542d1 100644 --- a/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/EntityConfigurationStatementServiceTest.kt +++ b/modules/services/src/commonTest/kotlin/com.sphereon.oid.fed.services/EntityConfigurationStatementServiceTest.kt @@ -1,7 +1,8 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.common.Constants -import com.sphereon.oid.fed.openapi.models.BaseJwk +import com.sphereon.oid.fed.openapi.models.EntityJwk +import com.sphereon.oid.fed.openapi.models.Jwk import com.sphereon.oid.fed.persistence.Persistence import com.sphereon.oid.fed.persistence.models.* import com.sphereon.oid.fed.services.config.AccountServiceConfig @@ -92,7 +93,7 @@ class EntityConfigurationStatementServiceTest { every { accountService.getAccountIdentifierByAccount(testAccount.toDTO()) } returns TEST_IDENTIFIER // Mock key service response - val testKey = BaseJwk(kid = TEST_KEY_ID, kty = "RSA", use = "sig") + val testKey = EntityJwk(kid = TEST_KEY_ID, kty = "RSA", use = "sig") every { jwkService.getKeys(testAccount.toDTO()) } returns arrayOf(testKey) // Mock empty results for optional components @@ -120,7 +121,7 @@ class EntityConfigurationStatementServiceTest { every { accountService.getAccountIdentifierByAccount(testAccount.toDTO()) } returns TEST_IDENTIFIER // Mock key service response - val testKey = BaseJwk(kid = TEST_KEY_ID, kty = "RSA", use = "sig") + val testKey = EntityJwk(kid = TEST_KEY_ID, kty = "RSA", use = "sig") every { jwkService.getKeys(testAccount.toDTO()) } returns arrayOf(testKey) // Mock KMS client response @@ -157,7 +158,7 @@ class EntityConfigurationStatementServiceTest { every { accountService.getAccountIdentifierByAccount(testAccount.toDTO()) } returns TEST_IDENTIFIER // Mock key service response - val testKey = BaseJwk(kid = TEST_KEY_ID, kty = "RSA", use = "sig") + val testKey = EntityJwk(kid = TEST_KEY_ID, kty = "RSA", use = "sig") every { jwkService.getKeys(testAccount.toDTO()) } returns arrayOf(testKey) // Mock KMS client response