From ba581e30daba1b3cdffdfbd9e85c1444fde08041 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Fri, 23 Aug 2024 18:37:10 +0530 Subject: [PATCH 01/26] feat: added KmsService and local KMS module --- modules/local-kms/build.gradle.kts | 23 +++++++++++++ modules/local-kms/src/main/kotlin/Main.kt | 5 +++ .../sphereon/oid/fed/services/KmsService.kt | 32 +++++++++++++++++++ settings.gradle.kts | 1 + 4 files changed, 61 insertions(+) create mode 100644 modules/local-kms/build.gradle.kts create mode 100644 modules/local-kms/src/main/kotlin/Main.kt create mode 100644 modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KmsService.kt diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts new file mode 100644 index 00000000..b1919cfd --- /dev/null +++ b/modules/local-kms/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + kotlin("jvm") version "2.0.0" +} + +group = "com.sphereon.oid.fed.kms.local" +version = "0.1.0" + +repositories { + mavenCentral() + mavenLocal() + google() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/modules/local-kms/src/main/kotlin/Main.kt b/modules/local-kms/src/main/kotlin/Main.kt new file mode 100644 index 00000000..d75aa9fd --- /dev/null +++ b/modules/local-kms/src/main/kotlin/Main.kt @@ -0,0 +1,5 @@ +package com.sphereon.oid.fed.kms.local + +fun main() { + println("Hello World!") +} \ No newline at end of file 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 new file mode 100644 index 00000000..8dc53cda --- /dev/null +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KmsService.kt @@ -0,0 +1,32 @@ +package com.sphereon.oid.fed.services + +import com.sphereon.oid.fed.persistence.models.Jwk + +class KmsService(private val provider: String) { + + private val kmsClient: KmsClient by lazy { + when (provider) { + //"local" -> LocalKmsClient() + //"aws" -> AwsKmsClient() + else -> throw IllegalArgumentException("Unsupported KMS provider: $provider") + } + } + + fun generateKeyPair(keyId: String): Jwk { + return kmsClient.generateKeyPair(keyId) + } + + fun sign(data: String, keyId: String): String { + return kmsClient.sign(data, keyId) + } + + fun verify(token: String, keyId: String): Boolean { + return kmsClient.verify(token, keyId) + } +} + +interface KmsClient { + fun generateKeyPair(keyId: String): Jwk + fun sign(data: String, keyId: String): String + fun verify(token: String, keyId: String): Boolean +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index bff086b8..90d48f4c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,3 +48,4 @@ include(":modules:federation-server") include(":modules:openapi") include(":modules:persistence") include(":modules:services") +include("modules:local-kms") From 0948b7a57c7b52ca45d7ebc192c6b01b4b978c63 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Fri, 23 Aug 2024 18:40:43 +0530 Subject: [PATCH 02/26] fix: linked service layer to local KMS module --- modules/local-kms/build.gradle.kts | 1 + .../src/main/kotlin/LocalKmsClient.kt | 19 +++++++++++++++++++ modules/local-kms/src/main/kotlin/Main.kt | 5 ----- 3 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 modules/local-kms/src/main/kotlin/LocalKmsClient.kt delete mode 100644 modules/local-kms/src/main/kotlin/Main.kt diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts index b1919cfd..753f261f 100644 --- a/modules/local-kms/build.gradle.kts +++ b/modules/local-kms/build.gradle.kts @@ -13,6 +13,7 @@ repositories { dependencies { testImplementation(kotlin("test")) + implementation(projects.modules.services) } tasks.test { diff --git a/modules/local-kms/src/main/kotlin/LocalKmsClient.kt b/modules/local-kms/src/main/kotlin/LocalKmsClient.kt new file mode 100644 index 00000000..6ddd0efd --- /dev/null +++ b/modules/local-kms/src/main/kotlin/LocalKmsClient.kt @@ -0,0 +1,19 @@ +package com.sphereon.oid.fed.kms.local + +import com.sphereon.oid.fed.persistence.models.Jwk +import com.sphereon.oid.fed.services.KmsClient + +class LocalKmsClient : KmsClient { + + override fun generateKeyPair(keyId: String): Jwk { + TODO("Not yet implemented") + } + + override fun sign(data: String, keyId: String): String { + TODO("Not yet implemented") + } + + override fun verify(token: String, keyId: String): Boolean { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/modules/local-kms/src/main/kotlin/Main.kt b/modules/local-kms/src/main/kotlin/Main.kt deleted file mode 100644 index d75aa9fd..00000000 --- a/modules/local-kms/src/main/kotlin/Main.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.sphereon.oid.fed.kms.local - -fun main() { - println("Hello World!") -} \ No newline at end of file From 26c7ce6c3f5c801fbbe89a8695f8a5e1e7a19e22 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Wed, 28 Aug 2024 11:48:23 +0530 Subject: [PATCH 03/26] feat: Setup Spring JDBC for local kms --- .docker/local-kms/Dockerfile | 19 +++++++++++++++++++ docker-compose.yaml | 17 +++++++++++++++++ modules/local-kms/build.gradle.kts | 6 +++++- modules/local-kms/src/main/kotlin/Key.kt | 13 +++++++++++++ .../src/main/kotlin/KeyRepository.kt | 13 +++++++++++++ .../src/main/kotlin/LocalKmsClient.kt | 2 +- .../main/kotlin/LocalKmsDatabaseConnection.kt | 19 +++++++++++++++++++ modules/local-kms/src/main/resources/1.sql | 8 ++++++++ 8 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 .docker/local-kms/Dockerfile create mode 100644 modules/local-kms/src/main/kotlin/Key.kt create mode 100644 modules/local-kms/src/main/kotlin/KeyRepository.kt create mode 100644 modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt create mode 100644 modules/local-kms/src/main/resources/1.sql diff --git a/.docker/local-kms/Dockerfile b/.docker/local-kms/Dockerfile new file mode 100644 index 00000000..6258bb96 --- /dev/null +++ b/.docker/local-kms/Dockerfile @@ -0,0 +1,19 @@ +FROM openjdk:21-jdk as builder +RUN microdnf install findutils + +WORKDIR /app + +COPY . /app + +RUN chmod +x ./gradlew + +RUN ./gradlew :modules:local-kms:bootJar -x test -x allTests -x jsBrowserTest + +FROM openjdk:21-jdk as runner + +WORKDIR /app + +COPY .env .env +COPY --from=builder /app/modules/local-kms/build/libs/local-kms-0.0.1.jar ./local-kms-0.0.1.jar + +ENTRYPOINT ["java", "-jar", "local-kms-0.0.1.jar"] diff --git a/docker-compose.yaml b/docker-compose.yaml index ce0cc8bf..1da21f3d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -55,6 +55,23 @@ services: networks: - openid_network + local-kms: + build: + context: . + dockerfile: ./.docker/local-kms/Dockerfile + ports: + - "8082:8082" + container_name: openid-federation-local-kms + environment: + DATASOURCE_URL: ${DATASOURCE_URL} + DATASOURCE_USER: ${DATASOURCE_USER} + DATASOURCE_PASSWORD: ${DATASOURCE_PASSWORD} + depends_on: + db: + condition: service_healthy + networks: + - openid_network + networks: openid_network: driver: bridge diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts index 753f261f..e2070bdb 100644 --- a/modules/local-kms/build.gradle.kts +++ b/modules/local-kms/build.gradle.kts @@ -1,5 +1,7 @@ plugins { - kotlin("jvm") version "2.0.0" + alias(libs.plugins.springboot) + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.kotlinJvm) } group = "com.sphereon.oid.fed.kms.local" @@ -14,6 +16,8 @@ repositories { dependencies { testImplementation(kotlin("test")) implementation(projects.modules.services) + implementation(libs.springboot.data.jdbc) + testImplementation(libs.springboot.test) } tasks.test { diff --git a/modules/local-kms/src/main/kotlin/Key.kt b/modules/local-kms/src/main/kotlin/Key.kt new file mode 100644 index 00000000..d67884a6 --- /dev/null +++ b/modules/local-kms/src/main/kotlin/Key.kt @@ -0,0 +1,13 @@ +package com.sphereon.oid.fed.kms.local + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Table + + +@Table("keys") +data class Key( + @Id val id: String, + val privateKey: ByteArray, + val publicKey: ByteArray, + val algorithm: String +) diff --git a/modules/local-kms/src/main/kotlin/KeyRepository.kt b/modules/local-kms/src/main/kotlin/KeyRepository.kt new file mode 100644 index 00000000..b95f1269 --- /dev/null +++ b/modules/local-kms/src/main/kotlin/KeyRepository.kt @@ -0,0 +1,13 @@ +package com.sphereon.oid.fed.kms.local + +import org.springframework.data.jdbc.repository.query.Query +import org.springframework.data.repository.CrudRepository +import org.springframework.stereotype.Repository + +@Repository +interface KeyRepository : CrudRepository { + + @Query("SELECT * FROM keys WHERE id = :keyId") + fun findByKeyId(keyId: String): Key? +} \ No newline at end of file diff --git a/modules/local-kms/src/main/kotlin/LocalKmsClient.kt b/modules/local-kms/src/main/kotlin/LocalKmsClient.kt index 6ddd0efd..653e00c0 100644 --- a/modules/local-kms/src/main/kotlin/LocalKmsClient.kt +++ b/modules/local-kms/src/main/kotlin/LocalKmsClient.kt @@ -3,7 +3,7 @@ package com.sphereon.oid.fed.kms.local import com.sphereon.oid.fed.persistence.models.Jwk import com.sphereon.oid.fed.services.KmsClient -class LocalKmsClient : KmsClient { +class LocalKmsClient(private val database: LocalKmsDatabaseConnection) : KmsClient { override fun generateKeyPair(keyId: String): Jwk { TODO("Not yet implemented") diff --git a/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt b/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt new file mode 100644 index 00000000..b96e3d15 --- /dev/null +++ b/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt @@ -0,0 +1,19 @@ +package com.sphereon.oid.fed.kms.local + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Component +class LocalKmsDatabaseConnection @Autowired constructor(private val keyRepository: KeyRepository) { + + fun insertKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) { + val key = Key(keyId, privateKey, publicKey, algorithm) + keyRepository.save(key) + } + + fun getKey(keyId: String): Key { + return keyRepository.findByKeyId(keyId) ?: throw Exception("Key not found") + } + + // ... (Implement other methods like updateKey, deleteKey as needed) +} \ No newline at end of file diff --git a/modules/local-kms/src/main/resources/1.sql b/modules/local-kms/src/main/resources/1.sql new file mode 100644 index 00000000..374b0b56 --- /dev/null +++ b/modules/local-kms/src/main/resources/1.sql @@ -0,0 +1,8 @@ +CREATE TABLE keys ( + id VARCHAR(255) PRIMARY KEY, + private_key BYTEA NOT NULL, + public_key BYTEA NOT NULL, + algorithm VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file From a61373091f6ea21c186feaa336d945972444c2c5 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Wed, 28 Aug 2024 12:15:54 +0530 Subject: [PATCH 04/26] fix: added missing properties file --- .../local-kms/src/main/resources/application.properties | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/local-kms/src/main/resources/application.properties diff --git a/modules/local-kms/src/main/resources/application.properties b/modules/local-kms/src/main/resources/application.properties new file mode 100644 index 00000000..0ac4201e --- /dev/null +++ b/modules/local-kms/src/main/resources/application.properties @@ -0,0 +1,9 @@ +spring.config.import=optional:file:../../.env[.properties] +spring.application.name=OpenID Federation Server +spring.datasource.url=${DATASOURCE_URL} +spring.datasource.username=${DATASOURCE_USER} +spring.datasource.password=${DATASOURCE_PASSWORD} +# Mapping /actuator/health to /status +management.endpoints.web.base-path=/ +management.endpoints.web.path-mapping.health=status +server.port=8080 From 52b0c0dc506faec9952de64cccb2633488caee60 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Wed, 28 Aug 2024 12:42:31 +0530 Subject: [PATCH 05/26] fix: added missing function in LocalKmsDatabaseConnection --- .../src/main/kotlin/KeyRepository.kt | 3 +-- .../main/kotlin/LocalKmsDatabaseConnection.kt | 21 ++++++++++++++++--- .../src/main/resources/application.properties | 5 +++++ .../src/main/resources/{1.sql => schema.sql} | 0 4 files changed, 24 insertions(+), 5 deletions(-) rename modules/local-kms/src/main/resources/{1.sql => schema.sql} (100%) diff --git a/modules/local-kms/src/main/kotlin/KeyRepository.kt b/modules/local-kms/src/main/kotlin/KeyRepository.kt index b95f1269..b1ce65e7 100644 --- a/modules/local-kms/src/main/kotlin/KeyRepository.kt +++ b/modules/local-kms/src/main/kotlin/KeyRepository.kt @@ -5,8 +5,7 @@ import org.springframework.data.repository.CrudRepository import org.springframework.stereotype.Repository @Repository -interface KeyRepository : CrudRepository { +interface KeyRepository : CrudRepository { @Query("SELECT * FROM keys WHERE id = :keyId") fun findByKeyId(keyId: String): Key? diff --git a/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt b/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt index b96e3d15..b79d307d 100644 --- a/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt +++ b/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt @@ -1,6 +1,7 @@ package com.sphereon.oid.fed.kms.local import org.springframework.beans.factory.annotation.Autowired +import org.springframework.dao.EmptyResultDataAccessException import org.springframework.stereotype.Component @Component @@ -12,8 +13,22 @@ class LocalKmsDatabaseConnection @Autowired constructor(private val keyRepositor } fun getKey(keyId: String): Key { - return keyRepository.findByKeyId(keyId) ?: throw Exception("Key not found") + return keyRepository.findByKeyId(keyId) ?: throw KeyNotFoundException("Key with ID $keyId not found") } - // ... (Implement other methods like updateKey, deleteKey as needed) -} \ No newline at end of file + fun updateKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) { + val existingKey = keyRepository.findByKeyId(keyId) ?: throw KeyNotFoundException("Key with ID $keyId not found") + val updatedKey = existingKey.copy(privateKey = privateKey, publicKey = publicKey, algorithm = algorithm) + keyRepository.save(updatedKey) + } + + fun deleteKey(keyId: String) { + try { + keyRepository.deleteById(keyId) + } catch (e: EmptyResultDataAccessException) { + throw KeyNotFoundException("Key with ID $keyId not found") + } + } +} + +class KeyNotFoundException(message: String) : Exception(message) \ No newline at end of file diff --git a/modules/local-kms/src/main/resources/application.properties b/modules/local-kms/src/main/resources/application.properties index 0ac4201e..98062da0 100644 --- a/modules/local-kms/src/main/resources/application.properties +++ b/modules/local-kms/src/main/resources/application.properties @@ -3,7 +3,12 @@ spring.application.name=OpenID Federation Server spring.datasource.url=${DATASOURCE_URL} spring.datasource.username=${DATASOURCE_USER} spring.datasource.password=${DATASOURCE_PASSWORD} + # Mapping /actuator/health to /status management.endpoints.web.base-path=/ management.endpoints.web.path-mapping.health=status server.port=8080 + +# Spring Boot to execute the schema.sql script on startup +spring.sql.init.mode=always +spring.datasource.schema=classpath:schema.sql diff --git a/modules/local-kms/src/main/resources/1.sql b/modules/local-kms/src/main/resources/schema.sql similarity index 100% rename from modules/local-kms/src/main/resources/1.sql rename to modules/local-kms/src/main/resources/schema.sql From e6d167fcae49a8b48cda71d24e68675a56a91913 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Thu, 29 Aug 2024 11:26:05 +0530 Subject: [PATCH 06/26] fix: change to SQLDelight --- .docker/local-kms/Dockerfile | 19 -------- docker-compose.yaml | 17 ------- modules/local-kms/build.gradle.kts | 43 +++++++++++++----- .../oid/fed/kms/local}/LocalKmsClient.kt | 2 +- .../oid/fed/kms/local/LocalKmsDatabase.kt | 12 +++++ .../sphereon/oid/fed/kms/local/models/Keys.sq | 6 +++ .../oid/fed/kms/local/LocalKmsDatabase.jvm.kt | 45 +++++++++++++++++++ modules/local-kms/src/main/kotlin/Key.kt | 13 ------ .../src/main/kotlin/KeyRepository.kt | 12 ----- .../main/kotlin/LocalKmsDatabaseConnection.kt | 34 -------------- .../src/main/resources/application.properties | 14 ------ .../local-kms/src/main/resources/schema.sql | 8 ---- 12 files changed, 95 insertions(+), 130 deletions(-) delete mode 100644 .docker/local-kms/Dockerfile rename modules/local-kms/src/{main/kotlin => commonMain/kotlin/com/sphereon/oid/fed/kms/local}/LocalKmsClient.kt (84%) create mode 100644 modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt create mode 100644 modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq create mode 100644 modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt delete mode 100644 modules/local-kms/src/main/kotlin/Key.kt delete mode 100644 modules/local-kms/src/main/kotlin/KeyRepository.kt delete mode 100644 modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt delete mode 100644 modules/local-kms/src/main/resources/application.properties delete mode 100644 modules/local-kms/src/main/resources/schema.sql diff --git a/.docker/local-kms/Dockerfile b/.docker/local-kms/Dockerfile deleted file mode 100644 index 6258bb96..00000000 --- a/.docker/local-kms/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM openjdk:21-jdk as builder -RUN microdnf install findutils - -WORKDIR /app - -COPY . /app - -RUN chmod +x ./gradlew - -RUN ./gradlew :modules:local-kms:bootJar -x test -x allTests -x jsBrowserTest - -FROM openjdk:21-jdk as runner - -WORKDIR /app - -COPY .env .env -COPY --from=builder /app/modules/local-kms/build/libs/local-kms-0.0.1.jar ./local-kms-0.0.1.jar - -ENTRYPOINT ["java", "-jar", "local-kms-0.0.1.jar"] diff --git a/docker-compose.yaml b/docker-compose.yaml index 1da21f3d..ce0cc8bf 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -55,23 +55,6 @@ services: networks: - openid_network - local-kms: - build: - context: . - dockerfile: ./.docker/local-kms/Dockerfile - ports: - - "8082:8082" - container_name: openid-federation-local-kms - environment: - DATASOURCE_URL: ${DATASOURCE_URL} - DATASOURCE_USER: ${DATASOURCE_USER} - DATASOURCE_PASSWORD: ${DATASOURCE_PASSWORD} - depends_on: - db: - condition: service_healthy - networks: - - openid_network - networks: openid_network: driver: bridge diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts index e2070bdb..39d3c9f0 100644 --- a/modules/local-kms/build.gradle.kts +++ b/modules/local-kms/build.gradle.kts @@ -1,7 +1,6 @@ plugins { - alias(libs.plugins.springboot) - alias(libs.plugins.springDependencyManagement) - alias(libs.plugins.kotlinJvm) + kotlin("multiplatform") version "2.0.0" + id("app.cash.sqldelight") version "2.0.2" } group = "com.sphereon.oid.fed.kms.local" @@ -13,16 +12,36 @@ repositories { google() } -dependencies { - testImplementation(kotlin("test")) - implementation(projects.modules.services) - implementation(libs.springboot.data.jdbc) - testImplementation(libs.springboot.test) +sqldelight { + databases { + create("Database") { + packageName = "com.sphereon.oid.fed.kms.local" + dialect("app.cash.sqldelight:postgresql-dialect:2.0.2") + schemaOutputDirectory = file("src/commonMain/resources/db/migration") + migrationOutputDirectory = file("src/commonMain/resources/db/migration") + deriveSchemaFromMigrations = true + migrationOutputFileFormat = ".sql" + srcDirs.from( + "src/commonMain/sqldelight" + ) + } + } } -tasks.test { - useJUnitPlatform() -} kotlin { - jvmToolchain(21) + jvm() + + sourceSets { + commonMain { + dependencies { + implementation(projects.modules.services) + } + } + + jvmMain { + dependencies { + implementation("app.cash.sqldelight:sqlite-driver:2.0.2") + } + } + } } \ No newline at end of file diff --git a/modules/local-kms/src/main/kotlin/LocalKmsClient.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsClient.kt similarity index 84% rename from modules/local-kms/src/main/kotlin/LocalKmsClient.kt rename to modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsClient.kt index 653e00c0..52702ff1 100644 --- a/modules/local-kms/src/main/kotlin/LocalKmsClient.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsClient.kt @@ -3,7 +3,7 @@ package com.sphereon.oid.fed.kms.local import com.sphereon.oid.fed.persistence.models.Jwk import com.sphereon.oid.fed.services.KmsClient -class LocalKmsClient(private val database: LocalKmsDatabaseConnection) : KmsClient { +class LocalKmsClient(private val database: LocalKmsDatabase) : KmsClient { override fun generateKeyPair(keyId: String): Jwk { TODO("Not yet implemented") diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt new file mode 100644 index 00000000..3ca459a6 --- /dev/null +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt @@ -0,0 +1,12 @@ +package com.sphereon.oid.fed.kms.local + +import com.sphereon.oid.fed.kms.local.models.Keys + +expect class LocalKmsDatabase { + fun getKey(keyId: String): Keys + fun insertKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) + fun updateKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) + fun deleteKey(keyId: String) +} + +class KeyNotFoundException(message: String) : Exception(message) \ No newline at end of file diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq new file mode 100644 index 00000000..7316cd85 --- /dev/null +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq @@ -0,0 +1,6 @@ +CREATE TABLE keys ( + id TEXT PRIMARY KEY, + private_key TEXT NOT NULL, + public_key TEXT NOT NULL, + algorithm TEXT NOT NULL +); \ No newline at end of file diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt new file mode 100644 index 00000000..8301fb1c --- /dev/null +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt @@ -0,0 +1,45 @@ +package com.sphereon.oid.fed.kms.local + +import app.cash.sqldelight.db.SqlDriver +import com.sphereon.oid.fed.kms.local.models.Keys +import com.sphereon.oid.fed.persistence.Constants +import com.sphereon.oid.fed.persistence.database.PlatformSqlDriver + + +actual class LocalKmsDatabase { + + init { + val driver = getDriver() + + val database = Database(driver) + + } + + private fun getDriver(): SqlDriver { + return PlatformSqlDriver().createPostgresDriver( + System.getenv(Constants.DATASOURCE_URL), + System.getenv(Constants.DATASOURCE_USER), + System.getenv(Constants.DATASOURCE_PASSWORD) + ) + } + + actual fun getKey(keyId: String): Keys { + TODO("Not yet implemented") + } + + actual fun insertKey( + keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String + ) { + TODO("Not yet implemented") + } + + actual fun updateKey( + keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String + ) { + TODO("Not yet implemented") + } + + actual fun deleteKey(keyId: String) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/modules/local-kms/src/main/kotlin/Key.kt b/modules/local-kms/src/main/kotlin/Key.kt deleted file mode 100644 index d67884a6..00000000 --- a/modules/local-kms/src/main/kotlin/Key.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.sphereon.oid.fed.kms.local - -import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Table - - -@Table("keys") -data class Key( - @Id val id: String, - val privateKey: ByteArray, - val publicKey: ByteArray, - val algorithm: String -) diff --git a/modules/local-kms/src/main/kotlin/KeyRepository.kt b/modules/local-kms/src/main/kotlin/KeyRepository.kt deleted file mode 100644 index b1ce65e7..00000000 --- a/modules/local-kms/src/main/kotlin/KeyRepository.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.sphereon.oid.fed.kms.local - -import org.springframework.data.jdbc.repository.query.Query -import org.springframework.data.repository.CrudRepository -import org.springframework.stereotype.Repository - -@Repository -interface KeyRepository : CrudRepository { - - @Query("SELECT * FROM keys WHERE id = :keyId") - fun findByKeyId(keyId: String): Key? -} \ No newline at end of file diff --git a/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt b/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt deleted file mode 100644 index b79d307d..00000000 --- a/modules/local-kms/src/main/kotlin/LocalKmsDatabaseConnection.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.sphereon.oid.fed.kms.local - -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.dao.EmptyResultDataAccessException -import org.springframework.stereotype.Component - -@Component -class LocalKmsDatabaseConnection @Autowired constructor(private val keyRepository: KeyRepository) { - - fun insertKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) { - val key = Key(keyId, privateKey, publicKey, algorithm) - keyRepository.save(key) - } - - fun getKey(keyId: String): Key { - return keyRepository.findByKeyId(keyId) ?: throw KeyNotFoundException("Key with ID $keyId not found") - } - - fun updateKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) { - val existingKey = keyRepository.findByKeyId(keyId) ?: throw KeyNotFoundException("Key with ID $keyId not found") - val updatedKey = existingKey.copy(privateKey = privateKey, publicKey = publicKey, algorithm = algorithm) - keyRepository.save(updatedKey) - } - - fun deleteKey(keyId: String) { - try { - keyRepository.deleteById(keyId) - } catch (e: EmptyResultDataAccessException) { - throw KeyNotFoundException("Key with ID $keyId not found") - } - } -} - -class KeyNotFoundException(message: String) : Exception(message) \ No newline at end of file diff --git a/modules/local-kms/src/main/resources/application.properties b/modules/local-kms/src/main/resources/application.properties deleted file mode 100644 index 98062da0..00000000 --- a/modules/local-kms/src/main/resources/application.properties +++ /dev/null @@ -1,14 +0,0 @@ -spring.config.import=optional:file:../../.env[.properties] -spring.application.name=OpenID Federation Server -spring.datasource.url=${DATASOURCE_URL} -spring.datasource.username=${DATASOURCE_USER} -spring.datasource.password=${DATASOURCE_PASSWORD} - -# Mapping /actuator/health to /status -management.endpoints.web.base-path=/ -management.endpoints.web.path-mapping.health=status -server.port=8080 - -# Spring Boot to execute the schema.sql script on startup -spring.sql.init.mode=always -spring.datasource.schema=classpath:schema.sql diff --git a/modules/local-kms/src/main/resources/schema.sql b/modules/local-kms/src/main/resources/schema.sql deleted file mode 100644 index 374b0b56..00000000 --- a/modules/local-kms/src/main/resources/schema.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE keys ( - id VARCHAR(255) PRIMARY KEY, - private_key BYTEA NOT NULL, - public_key BYTEA NOT NULL, - algorithm VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); \ No newline at end of file From 659c4c9ac6bbe8210e8d2fbdc82e6805f212165a Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Thu, 29 Aug 2024 12:01:29 +0530 Subject: [PATCH 07/26] fix: Fixed binary data store for Postgres --- .../com/sphereon/oid/fed/kms/local/models/1.sqm | 6 ++++++ .../com/sphereon/oid/fed/kms/local/models/Keys.sq | 14 ++++++++------ .../oid/fed/kms/local/LocalKmsDatabase.jvm.kt | 12 +++++++----- 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm new file mode 100644 index 00000000..b0d5435c --- /dev/null +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm @@ -0,0 +1,6 @@ +CREATE TABLE Keys ( + id TEXT PRIMARY KEY, + private_key BYTEA NOT NULL, + public_key BYTEA NOT NULL, + algorithm TEXT NOT NULL +); \ No newline at end of file diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq index 7316cd85..6dcb3c63 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq @@ -1,6 +1,8 @@ -CREATE TABLE keys ( - id TEXT PRIMARY KEY, - private_key TEXT NOT NULL, - public_key TEXT NOT NULL, - algorithm TEXT NOT NULL -); \ No newline at end of file +findAll: +SELECT * FROM Keys; + +create: +INSERT INTO Keys (id, private_key, public_key, algorithm) VALUES (?, ?, ?, ?) RETURNING *; + +findById: +SELECT * FROM Keys WHERE id = ?; diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt index 8301fb1c..04d10c28 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt @@ -8,11 +8,12 @@ import com.sphereon.oid.fed.persistence.database.PlatformSqlDriver actual class LocalKmsDatabase { + var database: Database + init { val driver = getDriver() - val database = Database(driver) - + database = Database(driver) } private fun getDriver(): SqlDriver { @@ -24,13 +25,14 @@ actual class LocalKmsDatabase { } actual fun getKey(keyId: String): Keys { - TODO("Not yet implemented") + return database.keysQueries.findById(keyId).executeAsOneOrNull() + ?: throw KeyNotFoundException("$keyId not found") } actual fun insertKey( keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String ) { - TODO("Not yet implemented") + database.keysQueries.create(keyId, privateKey, publicKey, algorithm).executeAsOneOrNull() } actual fun updateKey( @@ -42,4 +44,4 @@ actual class LocalKmsDatabase { actual fun deleteKey(keyId: String) { TODO("Not yet implemented") } -} \ No newline at end of file +} From 8d4b9fa71fe3d8090f74e40a747165655be09d95 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Thu, 29 Aug 2024 12:08:23 +0530 Subject: [PATCH 08/26] feat: Added query for delete key --- .../com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt | 1 - .../com/sphereon/oid/fed/kms/local/models/1.sqm | 3 ++- .../com/sphereon/oid/fed/kms/local/models/Keys.sq | 3 +++ .../sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt | 10 ++-------- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt index 3ca459a6..98cc7901 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt @@ -5,7 +5,6 @@ import com.sphereon.oid.fed.kms.local.models.Keys expect class LocalKmsDatabase { fun getKey(keyId: String): Keys fun insertKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) - fun updateKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) fun deleteKey(keyId: String) } diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm index b0d5435c..6bf90ee4 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm @@ -2,5 +2,6 @@ CREATE TABLE Keys ( id TEXT PRIMARY KEY, private_key BYTEA NOT NULL, public_key BYTEA NOT NULL, - algorithm TEXT NOT NULL + algorithm TEXT NOT NULL, + deleted_at TIMESTAMP ); \ No newline at end of file diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq index 6dcb3c63..34956c85 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq @@ -6,3 +6,6 @@ INSERT INTO Keys (id, private_key, public_key, algorithm) VALUES (?, ?, ?, ?) RE findById: SELECT * FROM Keys WHERE id = ?; + +delete: +UPDATE Keys SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?; \ No newline at end of file diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt index 04d10c28..261b7a5a 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt @@ -8,7 +8,7 @@ import com.sphereon.oid.fed.persistence.database.PlatformSqlDriver actual class LocalKmsDatabase { - var database: Database + private var database: Database init { val driver = getDriver() @@ -35,13 +35,7 @@ actual class LocalKmsDatabase { database.keysQueries.create(keyId, privateKey, publicKey, algorithm).executeAsOneOrNull() } - actual fun updateKey( - keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String - ) { - TODO("Not yet implemented") - } - actual fun deleteKey(keyId: String) { - TODO("Not yet implemented") + database.keysQueries.delete(keyId) } } From 5b3e9469f0c22abb5688ead8bcac03c65db9c9e3 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Thu, 29 Aug 2024 16:25:01 +0530 Subject: [PATCH 09/26] feat: changed dependencies --- modules/local-kms/build.gradle.kts | 6 +++-- .../sphereon/oid/fed/kms/local/Constants.kt | 10 ++++++++ .../local/{ => database}/LocalKmsDatabase.kt | 4 ++-- .../kms/local/database/PlatformSqlDriver.kt | 8 +++++++ .../{ => database}/LocalKmsDatabase.jvm.kt | 6 ++--- .../kms/local/database/PlatformSqlDriver.kt | 23 +++++++++++++++++++ modules/services/build.gradle.kts | 1 + .../sphereon/oid/fed/services/KmsService.kt | 12 ++++------ .../oid/fed/services}/LocalKmsClient.kt | 10 ++++---- 9 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt rename modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/{ => database}/LocalKmsDatabase.kt (77%) create mode 100644 modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt rename modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/{ => database}/LocalKmsDatabase.jvm.kt (86%) create mode 100644 modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt rename modules/{local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local => services/src/commonMain/kotlin/com/sphereon/oid/fed/services}/LocalKmsClient.kt (55%) diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts index 39d3c9f0..a86c522b 100644 --- a/modules/local-kms/build.gradle.kts +++ b/modules/local-kms/build.gradle.kts @@ -34,13 +34,15 @@ kotlin { sourceSets { commonMain { dependencies { - implementation(projects.modules.services) + api(projects.modules.openapi) } } jvmMain { dependencies { - implementation("app.cash.sqldelight:sqlite-driver:2.0.2") + implementation("app.cash.sqldelight:jdbc-driver:2.0.2") + implementation("com.zaxxer:HikariCP:5.1.0") + implementation("org.postgresql:postgresql:42.7.3") } } } diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt new file mode 100644 index 00000000..421da153 --- /dev/null +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt @@ -0,0 +1,10 @@ +package com.sphereon.oid.fed.kms.local + +class Constants { + companion object { + const val DATASOURCE_URL = "DATASOURCE_URL" + const val DATASOURCE_USER = "DATASOURCE_USER" + const val DATASOURCE_PASSWORD = "DATASOURCE_PASSWORD" + const val SQLITE_IS_NOT_SUPPORTED_IN_JVM = "SQLite is not supported in JVM" + } +} diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt similarity index 77% rename from modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt rename to modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt index 98cc7901..6b660d7a 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt @@ -1,8 +1,8 @@ -package com.sphereon.oid.fed.kms.local +package com.sphereon.oid.fed.kms.local.database import com.sphereon.oid.fed.kms.local.models.Keys -expect class LocalKmsDatabase { +expect class LocalKmsDatabase() { fun getKey(keyId: String): Keys fun insertKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) fun deleteKey(keyId: String) diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt new file mode 100644 index 00000000..fefec3c7 --- /dev/null +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt @@ -0,0 +1,8 @@ +package com.sphereon.oid.fed.kms.local.database + +import app.cash.sqldelight.db.SqlDriver + +expect class PlatformSqlDriver { + fun createPostgresDriver(url: String, username: String, password: String): SqlDriver + fun createSqliteDriver(path: String): SqlDriver +} \ No newline at end of file diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt similarity index 86% rename from modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt rename to modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt index 261b7a5a..3b834f4e 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsDatabase.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt @@ -1,9 +1,9 @@ -package com.sphereon.oid.fed.kms.local +package com.sphereon.oid.fed.kms.local.database import app.cash.sqldelight.db.SqlDriver +import com.sphereon.oid.fed.kms.local.Constants +import com.sphereon.oid.fed.kms.local.Database import com.sphereon.oid.fed.kms.local.models.Keys -import com.sphereon.oid.fed.persistence.Constants -import com.sphereon.oid.fed.persistence.database.PlatformSqlDriver actual class LocalKmsDatabase { diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt new file mode 100644 index 00000000..6a76e099 --- /dev/null +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/PlatformSqlDriver.kt @@ -0,0 +1,23 @@ +package com.sphereon.oid.fed.kms.local.database + +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.asJdbcDriver +import com.sphereon.oid.fed.kms.local.Constants +import com.zaxxer.hikari.HikariConfig +import com.zaxxer.hikari.HikariDataSource + +actual class PlatformSqlDriver { + actual fun createPostgresDriver(url: String, username: String, password: String): SqlDriver { + val config = HikariConfig() + config.jdbcUrl = url + config.username = username + config.password = password + + val dataSource = HikariDataSource(config) + return dataSource.asJdbcDriver() + } + + actual fun createSqliteDriver(path: String): SqlDriver { + throw UnsupportedOperationException(Constants.SQLITE_IS_NOT_SUPPORTED_IN_JVM) + } +} \ No newline at end of file diff --git a/modules/services/build.gradle.kts b/modules/services/build.gradle.kts index aa45dd97..56eeb580 100644 --- a/modules/services/build.gradle.kts +++ b/modules/services/build.gradle.kts @@ -21,6 +21,7 @@ kotlin { api(projects.modules.openapi) api(projects.modules.persistence) api(projects.modules.openidFederationCommon) + api(projects.modules.localKms) implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.11") } } 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 8dc53cda..b8fae3ac 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,15 +1,13 @@ package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.persistence.models.Jwk +import com.sphereon.oid.fed.openapi.models.Jwk + class KmsService(private val provider: String) { - private val kmsClient: KmsClient by lazy { - when (provider) { - //"local" -> LocalKmsClient() - //"aws" -> AwsKmsClient() - else -> throw IllegalArgumentException("Unsupported KMS provider: $provider") - } + private val kmsClient: KmsClient = when (provider) { + "local" -> LocalKmsClient() + else -> throw IllegalArgumentException("Unsupported KMS provider: $provider") } fun generateKeyPair(keyId: String): Jwk { diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsClient.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt similarity index 55% rename from modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsClient.kt rename to modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt index 52702ff1..610557fa 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKmsClient.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt @@ -1,9 +1,11 @@ -package com.sphereon.oid.fed.kms.local +package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.persistence.models.Jwk -import com.sphereon.oid.fed.services.KmsClient +import com.sphereon.oid.fed.kms.local.database.LocalKmsDatabase +import com.sphereon.oid.fed.openapi.models.Jwk -class LocalKmsClient(private val database: LocalKmsDatabase) : KmsClient { +class LocalKmsClient : KmsClient { + + private val database: LocalKmsDatabase = LocalKmsDatabase() override fun generateKeyPair(keyId: String): Jwk { TODO("Not yet implemented") From 273f96e5773f67635b0ea17c5f1d745943699335 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Thu, 29 Aug 2024 18:19:08 +0530 Subject: [PATCH 10/26] feat: moved JWT, Jwk to local kms module --- modules/local-kms/build.gradle.kts | 15 +++++++++++++++ .../com/sphereon/oid/fed/kms/local}/jwk/Jwk.kt | 2 +- .../sphereon/oid/fed/kms/local}/jwt/JoseJwt.kt | 2 +- .../com/sphereon/oid/fed/kms/local/jwk}/Jwk.kt | 4 ++-- .../sphereon/oid/fed/kms/local}/jwt/JoseJwt.js.kt | 2 +- .../sphereon/oid/fed/kms/local}/jwk/Jwk.jvm.kt | 2 +- .../oid/fed/kms/local}/jwt/JoseJwt.jvm.kt | 2 +- .../oid/fed/kms/local}/jwt/JoseJwtTest.jvm.kt | 2 +- modules/openid-federation-common/build.gradle.kts | 3 --- .../com/sphereon/oid/fed/services/KeyService.kt | 2 +- .../oid/fed/services/KeyServiceTest.jvm.kt | 2 +- 11 files changed, 25 insertions(+), 13 deletions(-) rename modules/{openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common => local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local}/jwk/Jwk.kt (66%) rename modules/{openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common => local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local}/jwt/JoseJwt.kt (83%) rename modules/{openid-federation-common/src/jsMain/kotlin/com.sphereon.oid.fed.common.jwk => local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwk}/Jwk.kt (79%) rename modules/{openid-federation-common/src/jsMain/kotlin/com/sphereon/oid/fed/common => local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local}/jwt/JoseJwt.js.kt (97%) rename modules/{openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common => local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local}/jwk/Jwk.jvm.kt (95%) rename modules/{openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common => local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local}/jwt/JoseJwt.jvm.kt (96%) rename modules/{openid-federation-common/src/jvmTest/kotlin/com/sphereon/oid/fed/common => local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local}/jwt/JoseJwtTest.jvm.kt (96%) diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts index a86c522b..55f17451 100644 --- a/modules/local-kms/build.gradle.kts +++ b/modules/local-kms/build.gradle.kts @@ -43,6 +43,21 @@ kotlin { implementation("app.cash.sqldelight:jdbc-driver:2.0.2") implementation("com.zaxxer:HikariCP:5.1.0") implementation("org.postgresql:postgresql:42.7.3") + implementation("com.nimbusds:nimbus-jose-jwt:9.40") + } + } + +// jsMain { +// dependencies { +// implementation(npm("typescript", "5.5.3")) +// implementation(npm("jose", "5.6.3")) +// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") +// } +// } + + jvmTest { + dependencies { + implementation(kotlin("test-junit")) } } } diff --git a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/jwk/Jwk.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt similarity index 66% rename from modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/jwk/Jwk.kt rename to modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt index 03cbaee8..44cc96f6 100644 --- a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/jwk/Jwk.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt @@ -1,4 +1,4 @@ -package com.sphereon.oid.fed.common.jwk +package com.sphereon.oid.fed.kms.local.jwk import com.sphereon.oid.fed.openapi.models.Jwk diff --git a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt similarity index 83% rename from modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.kt rename to modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt index a6ccd627..5780a4ad 100644 --- a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt @@ -1,4 +1,4 @@ -package com.sphereon.oid.fed.common.jwt +package com.sphereon.oid.fed.kms.local.jwt expect class JwtHeader expect class JwtPayload diff --git a/modules/openid-federation-common/src/jsMain/kotlin/com.sphereon.oid.fed.common.jwk/Jwk.kt b/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt similarity index 79% rename from modules/openid-federation-common/src/jsMain/kotlin/com.sphereon.oid.fed.common.jwk/Jwk.kt rename to modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt index f9c5208c..71f7aa93 100644 --- a/modules/openid-federation-common/src/jsMain/kotlin/com.sphereon.oid.fed.common.jwk/Jwk.kt +++ b/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt @@ -1,6 +1,6 @@ -package com.sphereon.oid.fed.common.jwk +package com.sphereon.oid.fed.kms.local.jwk -import com.sphereon.oid.fed.common.jwt.Jose +import com.sphereon.oid.fed.kms.local.jwt.Jose import com.sphereon.oid.fed.openapi.models.Jwk @ExperimentalJsExport diff --git a/modules/openid-federation-common/src/jsMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.js.kt b/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt similarity index 97% rename from modules/openid-federation-common/src/jsMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.js.kt rename to modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt index 5429b9b5..7212d40c 100644 --- a/modules/openid-federation-common/src/jsMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.js.kt +++ b/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt @@ -1,4 +1,4 @@ -package com.sphereon.oid.fed.common.jwt +package com.sphereon.oid.fed.kms.local.jwt import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement import com.sphereon.oid.fed.openapi.models.JWTHeader diff --git a/modules/openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common/jwk/Jwk.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt similarity index 95% rename from modules/openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common/jwk/Jwk.jvm.kt rename to modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt index 873ddaba..3f4e77f8 100644 --- a/modules/openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common/jwk/Jwk.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt @@ -1,4 +1,4 @@ -package com.sphereon.oid.fed.common.jwk +package com.sphereon.oid.fed.kms.local.jwk import com.nimbusds.jose.Algorithm import com.nimbusds.jose.jwk.Curve diff --git a/modules/openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt similarity index 96% rename from modules/openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.jvm.kt rename to modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt index 377697ad..705e2663 100644 --- a/modules/openid-federation-common/src/jvmMain/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwt.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt @@ -1,4 +1,4 @@ -package com.sphereon.oid.fed.common.jwt +package com.sphereon.oid.fed.kms.local.jwt import com.nimbusds.jose.JWSHeader import com.nimbusds.jose.JWSSigner diff --git a/modules/openid-federation-common/src/jvmTest/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwtTest.jvm.kt b/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt similarity index 96% rename from modules/openid-federation-common/src/jvmTest/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwtTest.jvm.kt rename to modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt index 54e8ddc3..d14df6f7 100644 --- a/modules/openid-federation-common/src/jvmTest/kotlin/com/sphereon/oid/fed/common/jwt/JoseJwtTest.jvm.kt +++ b/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt @@ -1,4 +1,4 @@ -package com.sphereon.oid.fed.common.jwt +package com.sphereon.oid.fed.kms.local.jwt import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jose.jwk.gen.RSAKeyGenerator diff --git a/modules/openid-federation-common/build.gradle.kts b/modules/openid-federation-common/build.gradle.kts index 09aab985..a411df92 100644 --- a/modules/openid-federation-common/build.gradle.kts +++ b/modules/openid-federation-common/build.gradle.kts @@ -73,7 +73,6 @@ kotlin { dependencies { implementation("io.ktor:ktor-client-core-jvm:$ktorVersion") runtimeOnly("io.ktor:ktor-client-cio-jvm:$ktorVersion") - implementation("com.nimbusds:nimbus-jose-jwt:9.40") } } val jvmTest by getting { @@ -130,8 +129,6 @@ kotlin { dependencies { runtimeOnly("io.ktor:ktor-client-core-js:$ktorVersion") runtimeOnly("io.ktor:ktor-client-js:$ktorVersion") - implementation(npm("typescript", "5.5.3")) - implementation(npm("jose", "5.6.3")) implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC") } diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt index 9aac7a58..95ffde0f 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt @@ -1,6 +1,6 @@ package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.common.jwk.generateKeyPair +import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair import com.sphereon.oid.fed.openapi.models.JwkAdminDTO import com.sphereon.oid.fed.persistence.Persistence import com.sphereon.oid.fed.persistence.models.Jwk diff --git a/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt b/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt index a5668d1c..cdb367a1 100644 --- a/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt +++ b/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt @@ -1,6 +1,6 @@ package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.common.jwk.generateKeyPair +import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair import com.sphereon.oid.fed.services.extensions.decrypt import com.sphereon.oid.fed.services.extensions.encrypt import org.junit.Test From 08e5e16805895d9717f443cea7072f7b96abfa21 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Fri, 30 Aug 2024 12:18:52 +0530 Subject: [PATCH 11/26] feat: linked generate key pair and sign functions --- modules/local-kms/build.gradle.kts | 2 + .../sphereon/oid/fed/kms/local/LocalKms.kt | 28 ++++++++ .../kms/local/database/LocalKmsDatabase.kt | 2 +- .../sphereon/oid/fed/kms/local/jwt/JoseJwt.kt | 7 +- .../sphereon/oid/fed/kms/local/models/1.sqm | 4 +- .../sphereon/oid/fed/kms/local/models/Keys.sq | 2 +- .../oid/fed/kms/local/jwt/JoseJwt.js.kt | 10 +-- .../local/database/LocalKmsDatabase.jvm.kt | 6 +- .../oid/fed/kms/local/jwt/JoseJwt.jvm.kt | 36 ++++++---- .../oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt | 65 +++++++++---------- .../sphereon/oid/fed/services/KmsService.kt | 16 ++--- .../oid/fed/services/LocalKmsClient.kt | 15 +++-- 12 files changed, 113 insertions(+), 80 deletions(-) create mode 100644 modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt diff --git a/modules/local-kms/build.gradle.kts b/modules/local-kms/build.gradle.kts index 55f17451..c771ff60 100644 --- a/modules/local-kms/build.gradle.kts +++ b/modules/local-kms/build.gradle.kts @@ -35,6 +35,8 @@ kotlin { commonMain { dependencies { api(projects.modules.openapi) + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.1") } } 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 new file mode 100644 index 00000000..5b3d6e08 --- /dev/null +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt @@ -0,0 +1,28 @@ +package com.sphereon.oid.fed.kms.local + +import com.sphereon.oid.fed.kms.local.database.LocalKmsDatabase +import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair +import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.kms.local.jwt.sign +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject + +class LocalKms { + + private val database: LocalKmsDatabase = LocalKmsDatabase() + + fun generateKey(keyId: String) { + val jwk = generateKeyPair() + database.insertKey(keyId = keyId, privateKey = jwk.toString()) + } + + fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { + val jwk = database.getKey(keyId) + + return sign(header = header, payload = payload, key = Json.decodeFromString(jwk.private_key)) + } + + fun verify(token: String, keyId: String): Boolean { + TODO("Pending") + } +} \ No newline at end of file diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt index 6b660d7a..3caebd98 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt @@ -4,7 +4,7 @@ import com.sphereon.oid.fed.kms.local.models.Keys expect class LocalKmsDatabase() { fun getKey(keyId: String): Keys - fun insertKey(keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String) + fun insertKey(keyId: String, privateKey: String) fun deleteKey(keyId: String) } diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt index 5780a4ad..717dd0b7 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt @@ -1,7 +1,8 @@ package com.sphereon.oid.fed.kms.local.jwt -expect class JwtHeader -expect class JwtPayload +import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.openapi.models.Jwk +import kotlinx.serialization.json.JsonObject -expect fun sign(payload: JwtPayload, header: JwtHeader, opts: Map): String +expect fun sign(payload: JsonObject, header: JWTHeader, key: Jwk): String expect fun verify(jwt: String, key: Any, opts: Map): Boolean diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm index 6bf90ee4..403f4b65 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm @@ -1,7 +1,5 @@ CREATE TABLE Keys ( id TEXT PRIMARY KEY, - private_key BYTEA NOT NULL, - public_key BYTEA NOT NULL, - algorithm TEXT NOT NULL, + private_key TEXT NOT NULL, deleted_at TIMESTAMP ); \ No newline at end of file diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq index 34956c85..7644afc1 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq @@ -2,7 +2,7 @@ findAll: SELECT * FROM Keys; create: -INSERT INTO Keys (id, private_key, public_key, algorithm) VALUES (?, ?, ?, ?) RETURNING *; +INSERT INTO Keys (id, private_key) VALUES (?, ?) RETURNING *; findById: SELECT * FROM Keys WHERE id = ?; diff --git a/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt b/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt index 7212d40c..aa502766 100644 --- a/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt +++ b/modules/local-kms/src/jsMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.js.kt @@ -2,6 +2,7 @@ package com.sphereon.oid.fed.kms.local.jwt import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -26,17 +27,12 @@ external object Jose { fun jwtVerify(jwt: String, key: Any, options: dynamic = definedExternally): dynamic } -actual typealias JwtPayload = EntityConfigurationStatement -actual typealias JwtHeader = JWTHeader - @ExperimentalJsExport @JsExport actual fun sign( - payload: JwtPayload, - header: JwtHeader, - opts: Map + payload: JsonObject, header: JWTHeader, key: Jwk ): String { - val privateKey = opts["privateKey"] ?: throw IllegalArgumentException("JWK private key is required") + val privateKey = key.privateKey ?: throw IllegalArgumentException("JWK private key is required") return Jose.SignJWT(JSON.parse(Json.encodeToString(payload))) .setProtectedHeader(JSON.parse(Json.encodeToString(header))) diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt index 3b834f4e..6fee47ff 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt @@ -29,10 +29,8 @@ actual class LocalKmsDatabase { ?: throw KeyNotFoundException("$keyId not found") } - actual fun insertKey( - keyId: String, privateKey: ByteArray, publicKey: ByteArray, algorithm: String - ) { - database.keysQueries.create(keyId, privateKey, publicKey, algorithm).executeAsOneOrNull() + actual fun insertKey(keyId: String, privateKey: String) { + database.keysQueries.create(keyId, privateKey).executeAsOneOrNull() } actual fun deleteKey(keyId: String) { diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt index 705e2663..3936c803 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt @@ -1,29 +1,28 @@ package com.sphereon.oid.fed.kms.local.jwt -import com.nimbusds.jose.JWSHeader -import com.nimbusds.jose.JWSSigner -import com.nimbusds.jose.JWSVerifier +import com.nimbusds.jose.* import com.nimbusds.jose.crypto.RSASSASigner import com.nimbusds.jose.crypto.RSASSAVerifier import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jwt.JWTClaimsSet import com.nimbusds.jwt.SignedJWT +import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.openapi.models.Jwk +import kotlinx.serialization.json.JsonObject -actual typealias JwtPayload = JWTClaimsSet -actual typealias JwtHeader = JWSHeader actual fun sign( - payload: JwtPayload, - header: JwtHeader, - opts: Map + payload: JsonObject, + header: JWTHeader, + key: Jwk ): String { - val rsaJWK = opts["key"] as RSAKey? ?: throw IllegalArgumentException("The RSA key pair is required") + val rsaJWK = key.toRsaKey() val signer: JWSSigner = RSASSASigner(rsaJWK) val signedJWT = SignedJWT( - header, - payload + header.toJWSHeader(), + JWTClaimsSet.parse(payload.toString()) ) signedJWT.sign(signer) @@ -44,4 +43,17 @@ actual fun verify( } catch (e: Exception) { throw Exception("Couldn't verify the JWT Signature: ${e.message}", e) } -} \ No newline at end of file +} + +fun JWTHeader.toJWSHeader(): JWSHeader { + val type = typ + return JWSHeader.Builder(JWSAlgorithm.parse(alg)).apply { + type(JOSEObjectType(type)) + keyID(kid) + }.build() +} + +//TODO: Double check the logic +fun Jwk.toRsaKey(): RSAKey { + return RSAKey.parse(this.toString()) +} diff --git a/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt b/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt index d14df6f7..fa390edf 100644 --- a/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt +++ b/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt @@ -2,43 +2,40 @@ package com.sphereon.oid.fed.kms.local.jwt import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jose.jwk.gen.RSAKeyGenerator +import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement +import com.sphereon.oid.fed.openapi.models.JWKS +import com.sphereon.oid.fed.openapi.models.JWTHeader +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.encodeToJsonElement import kotlin.test.Test import kotlin.test.assertTrue class JoseJwtTest { - @Test - fun signTest() { - val key = RSAKeyGenerator(2048).keyID("key1").generate() - val signature = sign( - JwtPayload.parse( - mutableMapOf( - "iss" to "test" - ) - ), - JwtHeader.parse(mutableMapOf( - "typ" to "JWT", - "alg" to "RS256", - "kid" to key.keyID)), - mutableMapOf("key" to key) - ) - assertTrue { signature.startsWith("ey") } - } - - @Test - fun verifyTest() { - val kid = "key1" - val key: RSAKey = RSAKeyGenerator(2048).keyID(kid).generate() - val signature = sign( - JwtPayload.parse( - mutableMapOf("iss" to "test") - ), - JwtHeader.parse(mutableMapOf( - "typ" to "JWT", - "alg" to "RS256", - "kid" to key.keyID)), - mutableMapOf("key" to key) - ) - assertTrue { verify(signature, key, emptyMap()) } - } + //TODO Fix it +// @Test +// fun signTest() { +// val key = RSAKeyGenerator(2048).keyID("key1").generate() +// val entityStatement = +// EntityConfigurationStatement(iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = JWKS()) +// val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject +// val signature = sign( +// payload, JWTHeader(alg = "RS256", typ = "JWT", kid = key.keyID), mutableMapOf("key" to key) +// ) +// assertTrue { signature.startsWith("ey") } +// } +// +// @Test +// fun verifyTest() { +// val kid = "key1" +// val key: RSAKey = RSAKeyGenerator(2048).keyID(kid).generate() +// val entityStatement = +// EntityConfigurationStatement(iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = JWKS()) +// val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject +// val signature = sign( +// payload, JWTHeader(alg = "RS256", typ = "JWT", kid = key.keyID), mutableMapOf("key" to key) +// ) +// assertTrue { verify(signature, key, emptyMap()) } +// } } 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 b8fae3ac..772c7676 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,7 +1,7 @@ package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.openapi.models.Jwk - +import com.sphereon.oid.fed.openapi.models.JWTHeader +import kotlinx.serialization.json.JsonObject class KmsService(private val provider: String) { @@ -10,12 +10,12 @@ class KmsService(private val provider: String) { else -> throw IllegalArgumentException("Unsupported KMS provider: $provider") } - fun generateKeyPair(keyId: String): Jwk { - return kmsClient.generateKeyPair(keyId) + fun generateKeyPair(keyId: String) { + kmsClient.generateKeyPair(keyId) } - fun sign(data: String, keyId: String): String { - return kmsClient.sign(data, keyId) + fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { + return kmsClient.sign(header, payload, keyId) } fun verify(token: String, keyId: String): Boolean { @@ -24,7 +24,7 @@ class KmsService(private val provider: String) { } interface KmsClient { - fun generateKeyPair(keyId: String): Jwk - fun sign(data: String, keyId: String): String + fun generateKeyPair(keyId: String) + fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String fun verify(token: String, keyId: String): Boolean } \ No newline at end of file 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 610557fa..993c5eed 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 @@ -1,18 +1,19 @@ package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.kms.local.database.LocalKmsDatabase -import com.sphereon.oid.fed.openapi.models.Jwk +import com.sphereon.oid.fed.kms.local.LocalKms +import com.sphereon.oid.fed.openapi.models.JWTHeader +import kotlinx.serialization.json.JsonObject class LocalKmsClient : KmsClient { - private val database: LocalKmsDatabase = LocalKmsDatabase() + val localKms = LocalKms() - override fun generateKeyPair(keyId: String): Jwk { - TODO("Not yet implemented") + override fun generateKeyPair(keyId: String) { + return localKms.generateKey(keyId) } - override fun sign(data: String, keyId: String): String { - TODO("Not yet implemented") + override fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { + return localKms.sign(header, payload, keyId) } override fun verify(token: String, keyId: String): Boolean { From ede0e94ca3f110efa117dc4ce25f4551895c0a9e Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Fri, 30 Aug 2024 19:52:22 +0530 Subject: [PATCH 12/26] fix: fixed verify function --- .../kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt | 5 ++++- .../kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt | 2 +- .../kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt | 5 ++--- .../kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) 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 5b3d6e08..79b41024 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 @@ -4,6 +4,7 @@ import com.sphereon.oid.fed.kms.local.database.LocalKmsDatabase import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.kms.local.jwt.sign +import com.sphereon.oid.fed.kms.local.jwt.verify import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject @@ -23,6 +24,8 @@ class LocalKms { } fun verify(token: String, keyId: String): Boolean { - TODO("Pending") + val jwk = database.getKey(keyId) + + return verify(jwt = token, key = Json.decodeFromString(jwk.private_key)) } } \ No newline at end of file diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt index 717dd0b7..a4032967 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.kt @@ -5,4 +5,4 @@ import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.json.JsonObject expect fun sign(payload: JsonObject, header: JWTHeader, key: Jwk): String -expect fun verify(jwt: String, key: Any, opts: Map): Boolean +expect fun verify(jwt: String, key: Jwk): Boolean diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt index 3936c803..9b871668 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt @@ -31,11 +31,10 @@ actual fun sign( actual fun verify( jwt: String, - key: Any, - opts: Map + key: Jwk ): Boolean { try { - val rsaKey = key as RSAKey + val rsaKey = key.toRsaKey() val verifier: JWSVerifier = RSASSAVerifier(rsaKey) val signedJWT = SignedJWT.parse(jwt) val verified = signedJWT.verify(verifier) 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 993c5eed..7f03fb0a 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 @@ -17,6 +17,6 @@ class LocalKmsClient : KmsClient { } override fun verify(token: String, keyId: String): Boolean { - TODO("Not yet implemented") + return localKms.verify(token, keyId) } } \ No newline at end of file From c22d139a9fbddc6988f76339d083f7262d3624d5 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Fri, 30 Aug 2024 21:29:31 +0530 Subject: [PATCH 13/26] fix: updated sign and verify function with ECkey --- .../oid/fed/kms/local/jwt/JoseJwt.jvm.kt | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt index 9b871668..882c03db 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt @@ -1,9 +1,9 @@ package com.sphereon.oid.fed.kms.local.jwt import com.nimbusds.jose.* -import com.nimbusds.jose.crypto.RSASSASigner -import com.nimbusds.jose.crypto.RSASSAVerifier -import com.nimbusds.jose.jwk.RSAKey +import com.nimbusds.jose.crypto.ECDSASigner +import com.nimbusds.jose.crypto.ECDSAVerifier +import com.nimbusds.jose.jwk.ECKey import com.nimbusds.jwt.JWTClaimsSet import com.nimbusds.jwt.SignedJWT import com.sphereon.oid.fed.openapi.models.JWTHeader @@ -16,9 +16,9 @@ actual fun sign( header: JWTHeader, key: Jwk ): String { - val rsaJWK = key.toRsaKey() + val ecJWK = ECKey.parse(key.toString()) - val signer: JWSSigner = RSASSASigner(rsaJWK) + val signer: JWSSigner = ECDSASigner(ecJWK) val signedJWT = SignedJWT( header.toJWSHeader(), @@ -34,8 +34,8 @@ actual fun verify( key: Jwk ): Boolean { try { - val rsaKey = key.toRsaKey() - val verifier: JWSVerifier = RSASSAVerifier(rsaKey) + val ecKey = ECKey.parse(key.toString()) // Parse JWK into ECKey + val verifier: JWSVerifier = ECDSAVerifier(ecKey) val signedJWT = SignedJWT.parse(jwt) val verified = signedJWT.verify(verifier) return verified @@ -51,8 +51,3 @@ fun JWTHeader.toJWSHeader(): JWSHeader { keyID(kid) }.build() } - -//TODO: Double check the logic -fun Jwk.toRsaKey(): RSAKey { - return RSAKey.parse(this.toString()) -} From 52282999802c02444b98312ec73e8aedbe11cb05 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Sun, 1 Sep 2024 17:59:33 +0530 Subject: [PATCH 14/26] fix: Fixed jvm test for sign and verify --- .../oid/fed/kms/local/jwt/JoseJwt.jvm.kt | 21 +++--- .../oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt | 64 +++++++++++-------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt index 882c03db..a6d4ed96 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwt.jvm.kt @@ -8,21 +8,20 @@ import com.nimbusds.jwt.JWTClaimsSet import com.nimbusds.jwt.SignedJWT import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.openapi.models.Jwk +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject - actual fun sign( - payload: JsonObject, - header: JWTHeader, - key: Jwk + payload: JsonObject, header: JWTHeader, key: Jwk ): String { - val ecJWK = ECKey.parse(key.toString()) - + val jwkJsonString = Json.encodeToString(key) + val ecJWK = ECKey.parse(jwkJsonString) val signer: JWSSigner = ECDSASigner(ecJWK) + val jwsHeader = header.toJWSHeader() val signedJWT = SignedJWT( - header.toJWSHeader(), - JWTClaimsSet.parse(payload.toString()) + jwsHeader, JWTClaimsSet.parse(payload.toString()) ) signedJWT.sign(signer) @@ -30,11 +29,11 @@ actual fun sign( } actual fun verify( - jwt: String, - key: Jwk + jwt: String, key: Jwk ): Boolean { try { - val ecKey = ECKey.parse(key.toString()) // Parse JWK into ECKey + val jwkJsonString = Json.encodeToString(key) + val ecKey = ECKey.parse(jwkJsonString) val verifier: JWSVerifier = ECDSAVerifier(ecKey) val signedJWT = SignedJWT.parse(jwt) val verified = signedJWT.verify(verifier) diff --git a/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt b/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt index fa390edf..8e92a1b8 100644 --- a/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt +++ b/modules/local-kms/src/jvmTest/kotlin/com/sphereon/oid/fed/kms/local/jwt/JoseJwtTest.jvm.kt @@ -1,10 +1,12 @@ package com.sphereon.oid.fed.kms.local.jwt -import com.nimbusds.jose.jwk.RSAKey -import com.nimbusds.jose.jwk.gen.RSAKeyGenerator +import com.nimbusds.jose.Algorithm +import com.nimbusds.jose.JWSAlgorithm +import com.nimbusds.jose.jwk.Curve +import com.nimbusds.jose.jwk.gen.ECKeyGenerator import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement -import com.sphereon.oid.fed.openapi.models.JWKS import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.encodeToJsonElement @@ -13,29 +15,35 @@ import kotlin.test.assertTrue class JoseJwtTest { - //TODO Fix it -// @Test -// fun signTest() { -// val key = RSAKeyGenerator(2048).keyID("key1").generate() -// val entityStatement = -// EntityConfigurationStatement(iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = JWKS()) -// val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject -// val signature = sign( -// payload, JWTHeader(alg = "RS256", typ = "JWT", kid = key.keyID), mutableMapOf("key" to key) -// ) -// assertTrue { signature.startsWith("ey") } -// } -// -// @Test -// fun verifyTest() { -// val kid = "key1" -// val key: RSAKey = RSAKeyGenerator(2048).keyID(kid).generate() -// val entityStatement = -// EntityConfigurationStatement(iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = JWKS()) -// val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject -// val signature = sign( -// payload, JWTHeader(alg = "RS256", typ = "JWT", kid = key.keyID), mutableMapOf("key" to key) -// ) -// assertTrue { verify(signature, key, emptyMap()) } -// } + @Test + fun signTest() { + val key = ECKeyGenerator(Curve.P_256).keyID("key1").algorithm(Algorithm("ES256")).generate() + val jwk = key.toString() + val entityStatement = EntityConfigurationStatement( + iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = JsonObject(mapOf()) + ) + val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject + val signature = sign( + payload, + JWTHeader(alg = JWSAlgorithm.ES256.toString(), typ = "JWT", kid = key.keyID), + Json.decodeFromString(jwk) + ) + assertTrue { signature.startsWith("ey") } + } + + @Test + fun verifyTest() { + val key = ECKeyGenerator(Curve.P_256).keyID("key1").algorithm(Algorithm("ES256")).generate() + val jwk = key.toString() + val entityStatement = EntityConfigurationStatement( + iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = JsonObject(mapOf()) + ) + val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject + val signature = sign( + payload, + JWTHeader(alg = JWSAlgorithm.ES256.toString(), typ = "JWT", kid = key.keyID), + Json.decodeFromString(jwk) + ) + assertTrue { verify(signature, Json.decodeFromString(jwk)) } + } } From 282aadf86c4be79ffd15275fe558e1f36d862ea4 Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Mon, 2 Sep 2024 12:03:08 +0530 Subject: [PATCH 15/26] fix: Fixed verify parameter --- .../kotlin/com/sphereon/oid/fed/kms/local/LocalKms.kt | 7 +++---- .../kotlin/com/sphereon/oid/fed/services/KmsService.kt | 7 ++++--- .../kotlin/com/sphereon/oid/fed/services/LocalKmsClient.kt | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) 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 79b41024..00a14f7e 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 @@ -5,6 +5,7 @@ import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.kms.local.jwt.sign import com.sphereon.oid.fed.kms.local.jwt.verify +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject @@ -23,9 +24,7 @@ class LocalKms { return sign(header = header, payload = payload, key = Json.decodeFromString(jwk.private_key)) } - fun verify(token: String, keyId: String): Boolean { - val jwk = database.getKey(keyId) - - return verify(jwt = token, key = Json.decodeFromString(jwk.private_key)) + fun verify(token: String, jwk: Jwk): Boolean { + return verify(jwt = token, key = jwk) } } \ No newline at end of file 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 772c7676..a692c2db 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,7 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.json.JsonObject class KmsService(private val provider: String) { @@ -18,13 +19,13 @@ class KmsService(private val provider: String) { return kmsClient.sign(header, payload, keyId) } - fun verify(token: String, keyId: String): Boolean { - return kmsClient.verify(token, keyId) + fun verify(token: String, jwk: Jwk): Boolean { + return kmsClient.verify(token, jwk) } } interface KmsClient { fun generateKeyPair(keyId: String) fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String - fun verify(token: String, keyId: String): Boolean + fun verify(token: String, jwk: Jwk): Boolean } \ No newline at end of file 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 7f03fb0a..b5b8c5eb 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 @@ -2,11 +2,12 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.kms.local.LocalKms import com.sphereon.oid.fed.openapi.models.JWTHeader +import com.sphereon.oid.fed.openapi.models.Jwk import kotlinx.serialization.json.JsonObject class LocalKmsClient : KmsClient { - val localKms = LocalKms() + private val localKms = LocalKms() override fun generateKeyPair(keyId: String) { return localKms.generateKey(keyId) @@ -16,7 +17,7 @@ class LocalKmsClient : KmsClient { return localKms.sign(header, payload, keyId) } - override fun verify(token: String, keyId: String): Boolean { - return localKms.verify(token, keyId) + override fun verify(token: String, jwk: Jwk): Boolean { + return localKms.verify(token, jwk) } } \ No newline at end of file From 623d2be2c76286a1509d284ba5c6ac82c52dbccc Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Mon, 2 Sep 2024 15:47:52 +0530 Subject: [PATCH 16/26] fix: Added JWK object into payload body --- .../sphereon/oid/fed/kms/local/LocalKms.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) 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 00a14f7e..dc88b2c3 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,12 +2,11 @@ package com.sphereon.oid.fed.kms.local import com.sphereon.oid.fed.kms.local.database.LocalKmsDatabase import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair -import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.kms.local.jwt.sign import com.sphereon.oid.fed.kms.local.jwt.verify +import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.openapi.models.Jwk -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.* class LocalKms { @@ -20,8 +19,30 @@ class LocalKms { fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { val jwk = database.getKey(keyId) + val jwkString: String = Json.decodeFromString(jwk.private_key) + val jwkObject: Jwk = Json.decodeFromString(jwkString) + + // Adding necessary parameter is header + val mHeader = header.copy(alg = jwkObject.alg, kid = jwkObject.kid) + + // Adding JWKs object in payload + val mutablePayload = payload.toMutableMap() + mutablePayload["kid"] = JsonPrimitive(jwkObject.kid) + val keyArrayOfJwks = buildJsonObject { + putJsonArray("keys") { + addJsonObject { + put("kty", jwkObject.kty) + put("n", jwkObject.n) + put("e", jwkObject.e) + put("kid", jwkObject.kid) + put("use", jwkObject.use) + } + } + } + mutablePayload["jwks"] = keyArrayOfJwks + val mPayload = JsonObject(mutablePayload) - return sign(header = header, payload = payload, key = Json.decodeFromString(jwk.private_key)) + return sign(header = mHeader, payload = mPayload, key = jwkObject) } fun verify(token: String, jwk: Jwk): Boolean { From e9147e72ec0a9e9318d4f558fe9c3085809cad2d Mon Sep 17 00:00:00 2001 From: Robert Mathew Date: Mon, 2 Sep 2024 17:11:36 +0530 Subject: [PATCH 17/26] fix: Added signing for EntityConfigurationStatement --- .../EntityConfigurationStatementService.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 32351923..36a4e8ad 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 @@ -4,14 +4,17 @@ import com.sphereon.oid.fed.common.builder.EntityConfigurationStatementBuilder import com.sphereon.oid.fed.common.builder.FederationEntityMetadataBuilder import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement import com.sphereon.oid.fed.openapi.models.FederationEntityMetadata +import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.persistence.Persistence import com.sphereon.oid.fed.services.extensions.toJwkDTO +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject class EntityConfigurationStatementService { private val accountService = AccountService() private val keyService = KeyService() + private val kmsService = KmsService("local") private val entityConfigurationStatementQueries = Persistence.entityConfigurationStatementQueries private val accountQueries = Persistence.accountQueries private val subordinateQueries = Persistence.subordinateQueries @@ -64,12 +67,19 @@ class EntityConfigurationStatementService { val entityConfigurationStatement = findByUsername(accountUsername) - // @TO-DO JWT creation and signing + val entityConfigurationStatementStr = Json.encodeToString(entityConfigurationStatement) + val entityConfigurationStatementObject = Json.parseToJsonElement(entityConfigurationStatementStr).jsonObject + val key = "key_id" + val jwt = kmsService.sign( + payload = entityConfigurationStatementObject, + header = JWTHeader(typ = "entity-statement+jwt"), + keyId = key + ) entityConfigurationStatementQueries.create( account_id = account.id, expires_at = entityConfigurationStatement.exp.toLong(), - statement = Json.encodeToString(EntityConfigurationStatement.serializer(), entityConfigurationStatement) + statement = jwt ).executeAsOne() return entityConfigurationStatement From 0bd7594b24c281d2c488ae885bd72d3cd66fbbfc Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 00:02:42 +0200 Subject: [PATCH 18/26] feat: create Entity Configuration Statement JWT --- .env | 13 ++- docker-compose.yaml | 28 +++++ .../EntityConfigurationMetadataController.kt | 22 ++-- .../controllers/EntityStatementController.kt | 9 +- .../server/admin/controllers/KeyController.kt | 17 +-- .../sphereon/oid/fed/kms/local/Constants.kt | 6 +- .../sphereon/oid/fed/kms/local/LocalKms.kt | 43 ++++---- .../kms/local/database/LocalKmsDatabase.kt | 4 +- .../fed/kms/local/encryption/AesEncryption.kt | 31 ++++++ .../fed/kms/local/extensions/JwkExtension.kt | 20 ++++ .../com/sphereon/oid/fed/kms/local/jwk/Jwk.kt | 1 - .../sphereon/oid/fed/kms/local/models/1.sqm | 4 +- .../sphereon/oid/fed/kms/local/models/Keys.sq | 4 +- .../local/database/LocalKmsDatabase.jvm.kt | 46 +++++++- .../sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt | 3 +- .../com/sphereon/oid/fed/openapi/openapi.yaml | 12 ++- .../EntityConfigurationStatementBuilder.kt | 3 +- .../sphereon/oid/fed/persistence/models/2.sqm | 20 +--- .../sphereon/oid/fed/persistence/models/5.sqm | 2 +- .../models/EntityConfigurationMetadata.sq | 2 +- .../oid/fed/persistence/models/Key.sq | 24 +---- .../sphereon/oid/fed/services/Constants.kt | 1 + .../EntityConfigurationMetadataService.kt | 27 ++--- .../EntityConfigurationStatementService.kt | 39 ++++--- .../sphereon/oid/fed/services/KeyService.kt | 46 ++------ .../sphereon/oid/fed/services/KmsService.kt | 20 ++-- .../oid/fed/services/LocalKmsClient.kt | 9 +- .../EntityConfigurationMetadataExtension.kt | 18 ++++ .../fed/services/extensions/KeyExtensions.kt | 100 ++++++------------ .../services/extensions/KeyExtensions.jvm.kt | 29 ----- .../oid/fed/services/KeyServiceTest.jvm.kt | 57 ---------- 31 files changed, 327 insertions(+), 333 deletions(-) create mode 100644 modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/encryption/AesEncryption.kt create mode 100644 modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/extensions/JwkExtension.kt create mode 100644 modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt delete mode 100644 modules/services/src/jvmMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.jvm.kt delete mode 100644 modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt diff --git a/.env b/.env index adb8b036..15eeba69 100644 --- a/.env +++ b/.env @@ -1,6 +1,15 @@ +APP_KEY=Nit5tWts42QeCynT1Q476LyStDeSd4xb + +ROOT_IDENTIFIER=http://localhost:8080 + DATASOURCE_URL=jdbc:postgresql://db:5432/openid-federation-db DATASOURCE_USER=openid-federation-db-user DATASOURCE_PASSWORD=openid-federation-db-password DATASOURCE_DB=openid-federation-db -APP_KEY=Nit5tWts42QeCynT1Q476LyStDeSd4xb -ROOT_IDENTIFIER=http://localhost:8080 + +KMS_PROVIDER=local + +LOCAL_KMS_DATASOURCE_URL=jdbc:postgresql://local-kms-db:5432/openid-federation-local-kms-db +LOCAL_KMS_DATASOURCE_USER=openid-federation-local-kms-db-user +LOCAL_KMS_DATASOURCE_PASSWORD=openid-federation-local-kms-db-password +LOCAL_KMS_DATASOURCE_DB=openid-federation-local-kms-db diff --git a/docker-compose.yaml b/docker-compose.yaml index ce0cc8bf..36223302 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -18,6 +18,25 @@ services: timeout: 5s retries: 20 + local-kms-db: + image: postgres:latest + container_name: openid-federation-local-kms-datastore + environment: + POSTGRES_USER: ${LOCAL_KMS_DATASOURCE_USER} + POSTGRES_PASSWORD: ${LOCAL_KMS_DATASOURCE_PASSWORD} + POSTGRES_DB: ${LOCAL_KMS_DATASOURCE_DB} + ports: + - "5433:5432" + volumes: + - local_kms_data:/var/lib/postgresql/data + networks: + - openid_network + healthcheck: + test: [ "CMD-SHELL", "pg_isready -d ${LOCAL_KMS_DATASOURCE_DB} -U ${LOCAL_KMS_DATASOURCE_USER}" ] + interval: 3s + timeout: 5s + retries: 20 + federation-server: build: context: . @@ -49,9 +68,17 @@ services: DATASOURCE_USER: ${DATASOURCE_USER} DATASOURCE_PASSWORD: ${DATASOURCE_PASSWORD} APP_KEY: ${APP_KEY} + KMS_PROVIDER: ${KMS_PROVIDER} + LOCAL_KMS_DATASOURCE_URL: ${LOCAL_KMS_DATASOURCE_URL} + LOCAL_KMS_DATASOURCE_USER: ${LOCAL_KMS_DATASOURCE_USER} + LOCAL_KMS_DATASOURCE_PASSWORD: ${LOCAL_KMS_DATASOURCE_PASSWORD} + LOCAL_KMS_DATASOURCE_DB: ${LOCAL_KMS_DATASOURCE_DB} + depends_on: db: condition: service_healthy + local-kms-db: + condition: service_healthy networks: - openid_network @@ -61,3 +88,4 @@ networks: volumes: postgres_data: + local_kms_data: diff --git a/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityConfigurationMetadataController.kt b/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityConfigurationMetadataController.kt index 9caa1e74..a7ff56ce 100644 --- a/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityConfigurationMetadataController.kt +++ b/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityConfigurationMetadataController.kt @@ -1,9 +1,15 @@ package com.sphereon.oid.fed.server.admin.controllers import com.sphereon.oid.fed.openapi.models.CreateMetadataDTO -import com.sphereon.oid.fed.persistence.models.EntityConfigurationMetadata +import com.sphereon.oid.fed.openapi.models.EntityConfigurationMetadataDTO import com.sphereon.oid.fed.services.EntityConfigurationMetadataService -import org.springframework.web.bind.annotation.* +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/accounts/{accountUsername}/metadata") @@ -13,19 +19,19 @@ class EntityConfigurationMetadataController { @GetMapping fun get( @PathVariable accountUsername: String - ): Array { + ): Array { return entityConfigurationMetadataService.findByAccountUsername(accountUsername) } @PostMapping fun create( @PathVariable accountUsername: String, - @RequestBody metadata: CreateMetadataDTO - ): EntityConfigurationMetadata { + @RequestBody body: CreateMetadataDTO + ): EntityConfigurationMetadataDTO { return entityConfigurationMetadataService.createEntityConfigurationMetadata( accountUsername, - metadata.key, - metadata.value + body.key, + body.metadata ) } @@ -33,7 +39,7 @@ class EntityConfigurationMetadataController { fun delete( @PathVariable accountUsername: String, @PathVariable id: Int - ): EntityConfigurationMetadata { + ): EntityConfigurationMetadataDTO { return entityConfigurationMetadataService.deleteEntityConfigurationMetadata(accountUsername, id) } } diff --git a/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityStatementController.kt b/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityStatementController.kt index cf444b49..668bc76b 100644 --- a/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityStatementController.kt +++ b/modules/admin-server/src/main/kotlin/com/sphereon/oid/fed/server/admin/controllers/EntityStatementController.kt @@ -1,10 +1,12 @@ package com.sphereon.oid.fed.server.admin.controllers import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement +import com.sphereon.oid.fed.openapi.models.PublishEntityStatementDTO import com.sphereon.oid.fed.services.EntityConfigurationStatementService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -19,7 +21,10 @@ class EntityStatementController { } @PostMapping - fun publishEntityStatement(@PathVariable accountUsername: String): EntityConfigurationStatement { - return entityConfigurationStatementService.publishByUsername(accountUsername) + fun publishEntityStatement( + @PathVariable accountUsername: String, + @RequestBody body: PublishEntityStatementDTO? + ): String { + return entityConfigurationStatementService.publishByUsername(accountUsername, body?.dryRun ?: false) } } 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 9bc819b2..4ceb636a 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 @@ -2,8 +2,13 @@ package com.sphereon.oid.fed.server.admin.controllers import com.sphereon.oid.fed.openapi.models.JwkAdminDTO import com.sphereon.oid.fed.services.KeyService -import com.sphereon.oid.fed.services.extensions.toJwkAdminDTO -import org.springframework.web.bind.annotation.* +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/accounts/{accountUsername}/keys") @@ -13,13 +18,13 @@ class KeyController { @PostMapping fun create(@PathVariable accountUsername: String): JwkAdminDTO { val key = keyService.create(accountUsername) - return key.toJwkAdminDTO() + return key } @GetMapping - fun getKeys(@PathVariable accountUsername: String): List { + fun getKeys(@PathVariable accountUsername: String): Array { val keys = keyService.getKeys(accountUsername) - return keys.map { it.toJwkAdminDTO() } + return keys } @DeleteMapping("/{keyId}") @@ -30,4 +35,4 @@ class KeyController { ): JwkAdminDTO { return keyService.revokeKey(accountUsername, keyId, reason) } -} \ No newline at end of file +} diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt index 421da153..928a9356 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/Constants.kt @@ -2,9 +2,9 @@ package com.sphereon.oid.fed.kms.local class Constants { companion object { - const val DATASOURCE_URL = "DATASOURCE_URL" - const val DATASOURCE_USER = "DATASOURCE_USER" - const val DATASOURCE_PASSWORD = "DATASOURCE_PASSWORD" + const val LOCAL_KMS_DATASOURCE_URL = "LOCAL_KMS_DATASOURCE_URL" + const val LOCAL_KMS_DATASOURCE_USER = "LOCAL_KMS_DATASOURCE_USER" + const val LOCAL_KMS_DATASOURCE_PASSWORD = "LOCAL_KMS_DATASOURCE_PASSWORD" const val SQLITE_IS_NOT_SUPPORTED_IN_JVM = "SQLite is not supported in JVM" } } 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 dc88b2c3..eae176d6 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 @@ -1,51 +1,44 @@ 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.toJwkAdminDto 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 import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.openapi.models.Jwk -import kotlinx.serialization.json.* +import com.sphereon.oid.fed.openapi.models.JwkAdminDTO +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject class LocalKms { private val database: LocalKmsDatabase = LocalKmsDatabase() + private val aesEncryption: AesEncryption = AesEncryption() - fun generateKey(keyId: String) { + fun generateKey(): JwkAdminDTO { val jwk = generateKeyPair() - database.insertKey(keyId = keyId, privateKey = jwk.toString()) + + database.insertKey( + keyId = jwk.kid!!, + key = aesEncryption.encrypt(Json.encodeToString(Jwk.serializer(), jwk)) + ) + + return jwk.toJwkAdminDto() } fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { val jwk = database.getKey(keyId) - val jwkString: String = Json.decodeFromString(jwk.private_key) - val jwkObject: Jwk = Json.decodeFromString(jwkString) - // Adding necessary parameter is header + val jwkObject: Jwk = Json.decodeFromString(aesEncryption.decrypt(jwk.key)) + val mHeader = header.copy(alg = jwkObject.alg, kid = jwkObject.kid) - // Adding JWKs object in payload - val mutablePayload = payload.toMutableMap() - mutablePayload["kid"] = JsonPrimitive(jwkObject.kid) - val keyArrayOfJwks = buildJsonObject { - putJsonArray("keys") { - addJsonObject { - put("kty", jwkObject.kty) - put("n", jwkObject.n) - put("e", jwkObject.e) - put("kid", jwkObject.kid) - put("use", jwkObject.use) - } - } - } - mutablePayload["jwks"] = keyArrayOfJwks - val mPayload = JsonObject(mutablePayload) - - return sign(header = mHeader, payload = mPayload, key = jwkObject) + return sign(header = mHeader, payload = payload, key = jwkObject) } fun verify(token: String, jwk: Jwk): Boolean { return verify(jwt = token, key = jwk) } -} \ No newline at end of file +} diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt index 3caebd98..c698c30f 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.kt @@ -4,8 +4,8 @@ import com.sphereon.oid.fed.kms.local.models.Keys expect class LocalKmsDatabase() { fun getKey(keyId: String): Keys - fun insertKey(keyId: String, privateKey: String) + fun insertKey(keyId: String, key: String) fun deleteKey(keyId: String) } -class KeyNotFoundException(message: String) : Exception(message) \ No newline at end of file +class KeyNotFoundException(message: String) : Exception(message) diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/encryption/AesEncryption.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/encryption/AesEncryption.kt new file mode 100644 index 00000000..36f03f78 --- /dev/null +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/encryption/AesEncryption.kt @@ -0,0 +1,31 @@ +package com.sphereon.oid.fed.kms.local.encryption + +import java.util.* +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec + +private const val KEY_SIZE = 32 +private const val ALGORITHM = "AES" + +class AesEncryption { + + private val secretKey: SecretKeySpec = + SecretKeySpec(System.getenv("APP_KEY").padEnd(KEY_SIZE, '0').toByteArray(Charsets.UTF_8), ALGORITHM) + + fun encrypt(data: String): String { + val cipher = Cipher.getInstance(ALGORITHM) + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + + val encryptedValue = cipher.doFinal(data.toByteArray(Charsets.UTF_8)) + return Base64.getEncoder().encodeToString(encryptedValue) + } + + fun decrypt(data: String): String { + val cipher = Cipher.getInstance(ALGORITHM) + cipher.init(Cipher.DECRYPT_MODE, secretKey) + + val decodedValue = Base64.getDecoder().decode(data) + val decryptedValue = cipher.doFinal(decodedValue) + return String(decryptedValue, Charsets.UTF_8) + } +} diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/extensions/JwkExtension.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/extensions/JwkExtension.kt new file mode 100644 index 00000000..4876609d --- /dev/null +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/extensions/JwkExtension.kt @@ -0,0 +1,20 @@ +package com.sphereon.oid.fed.kms.local.extensions + +import com.sphereon.oid.fed.openapi.models.Jwk +import com.sphereon.oid.fed.openapi.models.JwkAdminDTO + +fun Jwk.toJwkAdminDto(): JwkAdminDTO = JwkAdminDTO( + kid = this.kid, + use = this.use, + crv = this.crv, + n = this.n, + e = this.e, + x = this.x, + y = this.y, + kty = this.kty, + alg = this.alg, + x5u = this.x5u, + x5t = this.x5t, + x5c = this.x5c, + x5tHashS256 = this.x5tS256 +) diff --git a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt index 44cc96f6..93f65ed3 100644 --- a/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt +++ b/modules/local-kms/src/commonMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.kt @@ -3,4 +3,3 @@ package com.sphereon.oid.fed.kms.local.jwk import com.sphereon.oid.fed.openapi.models.Jwk expect fun generateKeyPair(): Jwk - diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm index 403f4b65..aaee9711 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/1.sqm @@ -1,5 +1,5 @@ CREATE TABLE Keys ( id TEXT PRIMARY KEY, - private_key TEXT NOT NULL, + key TEXT NOT NULL, deleted_at TIMESTAMP -); \ No newline at end of file +); diff --git a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq index 7644afc1..d9677f09 100644 --- a/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq +++ b/modules/local-kms/src/commonMain/sqldelight/com/sphereon/oid/fed/kms/local/models/Keys.sq @@ -2,10 +2,10 @@ findAll: SELECT * FROM Keys; create: -INSERT INTO Keys (id, private_key) VALUES (?, ?) RETURNING *; +INSERT INTO Keys (id, key) VALUES (?, ?) RETURNING *; findById: SELECT * FROM Keys WHERE id = ?; delete: -UPDATE Keys SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?; \ No newline at end of file +UPDATE Keys SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?; diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt index 6fee47ff..7aab0e68 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/database/LocalKmsDatabase.jvm.kt @@ -1,5 +1,7 @@ package com.sphereon.oid.fed.kms.local.database +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlCursor import app.cash.sqldelight.db.SqlDriver import com.sphereon.oid.fed.kms.local.Constants import com.sphereon.oid.fed.kms.local.Database @@ -12,25 +14,59 @@ actual class LocalKmsDatabase { init { val driver = getDriver() + runMigrations(driver) database = Database(driver) } private fun getDriver(): SqlDriver { return PlatformSqlDriver().createPostgresDriver( - System.getenv(Constants.DATASOURCE_URL), - System.getenv(Constants.DATASOURCE_USER), - System.getenv(Constants.DATASOURCE_PASSWORD) + System.getenv(Constants.LOCAL_KMS_DATASOURCE_URL), + System.getenv(Constants.LOCAL_KMS_DATASOURCE_USER), + System.getenv(Constants.LOCAL_KMS_DATASOURCE_PASSWORD) ) } + private fun runMigrations(driver: SqlDriver) { + setupSchemaVersioningTable(driver) + + val currentVersion = getCurrentDatabaseVersion(driver) + val newVersion = Database.Schema.version + + if (currentVersion < newVersion) { + Database.Schema.migrate(driver, currentVersion, newVersion) + updateDatabaseVersion(driver, newVersion) + } + } + + private fun setupSchemaVersioningTable(driver: SqlDriver) { + driver.execute(null, "CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL)", 0) + } + + private fun getCurrentDatabaseVersion(driver: SqlDriver): Long { + val versionQuery = "SELECT version FROM schema_version ORDER BY version DESC LIMIT 1" + + val version = driver.executeQuery(null, versionQuery, parameters = 0, mapper = { cursor: SqlCursor -> + QueryResult.Value(if (cursor.next().value) cursor.getLong(0) else null) + }) + + return version.value ?: 0 + } + + private fun updateDatabaseVersion(driver: SqlDriver, newVersion: Long) { + val updateQuery = "INSERT INTO schema_version (version) VALUES (?)" + driver.execute(null, updateQuery, 1) { + bindLong(0, newVersion) + } + } + actual fun getKey(keyId: String): Keys { return database.keysQueries.findById(keyId).executeAsOneOrNull() ?: throw KeyNotFoundException("$keyId not found") } - actual fun insertKey(keyId: String, privateKey: String) { - database.keysQueries.create(keyId, privateKey).executeAsOneOrNull() + actual fun insertKey(keyId: String, key: String) { + database.keysQueries.create(keyId, key).executeAsOneOrNull() } actual fun deleteKey(keyId: String) { diff --git a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt index 3f4e77f8..78e9442d 100644 --- a/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt +++ b/modules/local-kms/src/jvmMain/kotlin/com/sphereon/oid/fed/kms/local/jwk/Jwk.jvm.kt @@ -7,11 +7,12 @@ import com.nimbusds.jose.jwk.gen.ECKeyGenerator import com.sphereon.oid.fed.openapi.models.Jwk import java.util.* + actual fun generateKeyPair(): Jwk { try { val ecKey: ECKey = ECKeyGenerator(Curve.P_256) .keyIDFromThumbprint(true) - .algorithm(Algorithm("EC")) + .algorithm(Algorithm("ES256")) .issueTime(Date()) .generate() diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml index 426a8b4b..c40ef801 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml @@ -2495,12 +2495,20 @@ components: type: string description: The metadata key. example: openid_relying_party - value: + metadata: additionalProperties: true description: The metadata object. required: - key - - value + - metadata + + PublishEntityStatementDTO: + type: object + properties: + dry-run: + type: boolean + description: If true, the statement will not be published. + example: false CreateAuthorityHintDTO: type: object diff --git a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementBuilder.kt b/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementBuilder.kt index f24a7e25..dab956da 100644 --- a/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementBuilder.kt +++ b/modules/openid-federation-common/src/commonMain/kotlin/com/sphereon/oid/fed/common/builder/EntityConfigurationStatementBuilder.kt @@ -32,7 +32,8 @@ class EntityConfigurationStatementBuilder { @OptIn(ExperimentalSerializationApi::class) private fun createJwks(jwks: Array): JsonObject { - val jsonArray: JsonArray = Json.encodeToJsonElement(ArraySerializer(JwkDTO.serializer()), jwks) as JsonArray + val jsonArray: JsonArray = + Json.encodeToJsonElement(ArraySerializer(JwkDTO.serializer()), jwks) as JsonArray return buildJsonObject { put("keys", jsonArray) diff --git a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/2.sqm b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/2.sqm index 61d5198e..4cdb7165 100644 --- a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/2.sqm +++ b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/2.sqm @@ -1,26 +1,8 @@ CREATE TABLE Jwk ( id SERIAL PRIMARY KEY, - uuid UUID DEFAULT gen_random_uuid(), account_id INT NOT NULL, - kty VARCHAR(10) NOT NULL, - crv VARCHAR(10), kid VARCHAR(255) UNIQUE, - x TEXT, - y TEXT, - d TEXT, - n TEXT, - e TEXT, - p TEXT, - q TEXT, - dp TEXT, - dq TEXT, - qi TEXT, - x5u TEXT, - x5c TEXT[], - x5t TEXT, - x5t_s256 TEXT, - alg VARCHAR(10), - use VARCHAR(10) NULL, + key TEXT NOT NULL, revoked_at TIMESTAMP, revoked_reason TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, diff --git a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/5.sqm b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/5.sqm index ef6a2bac..1b5c22f1 100644 --- a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/5.sqm +++ b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/5.sqm @@ -2,7 +2,7 @@ CREATE TABLE EntityConfigurationMetadata ( id SERIAL PRIMARY KEY, account_id INT NOT NULL, key TEXT NOT NULL, - value TEXT NOT NULL, + metadata TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMP, CONSTRAINT FK_ParentEntityConfigurationMetadata FOREIGN KEY (account_id) REFERENCES Account (id) diff --git a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/EntityConfigurationMetadata.sq b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/EntityConfigurationMetadata.sq index 72873978..bd083732 100644 --- a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/EntityConfigurationMetadata.sq +++ b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/EntityConfigurationMetadata.sq @@ -2,7 +2,7 @@ create: INSERT INTO EntityConfigurationMetadata ( account_id, key, - value + metadata ) VALUES (?, ?, ?) RETURNING *; delete: diff --git a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/Key.sq b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/Key.sq index 1db776f8..108a8dc0 100644 --- a/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/Key.sq +++ b/modules/persistence/src/commonMain/sqldelight/com/sphereon/oid/fed/persistence/models/Key.sq @@ -1,33 +1,15 @@ create: INSERT INTO Jwk ( account_id, - kty, - crv, kid, - x, - y, - d, - n, - e, - p, - q, - dp, - dq, - qi, - x5u, - x5c, - x5t, - x5t_s256, - alg, - use -) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *; + key +) VALUES (?, ?, ?) RETURNING *; revoke: UPDATE Jwk SET (revoked_at, revoked_reason) = (CURRENT_TIMESTAMP, ?) WHERE id = ?; findByAccountId: -SELECT * FROM Jwk WHERE account_id = ?; +SELECT * FROM Jwk WHERE account_id = ? AND revoked_at IS NULL ORDER BY created_at DESC; findById: SELECT * FROM Jwk WHERE id = ?; diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/Constants.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/Constants.kt index d4c04fe3..8df7a059 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/Constants.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/Constants.kt @@ -14,5 +14,6 @@ class Constants { const val AUTHORITY_HINT_NOT_FOUND = "Authority hint not found" const val FAILED_TO_DELETE_AUTHORITY_HINT = "Failed to delete authority hint" const val AUTHORITY_HINT_ALREADY_EXISTS = "Authority hint already exists" + const val NO_KEYS_FOUND = "No keys found" } } diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt index d5e26837..61c9261b 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt @@ -1,7 +1,8 @@ package com.sphereon.oid.fed.services +import com.sphereon.oid.fed.openapi.models.EntityConfigurationMetadataDTO import com.sphereon.oid.fed.persistence.Persistence -import com.sphereon.oid.fed.persistence.models.EntityConfigurationMetadata +import com.sphereon.oid.fed.services.extensions.toEntityConfigurationMetadataDTO import kotlinx.serialization.json.JsonObject class EntityConfigurationMetadataService { @@ -9,7 +10,7 @@ class EntityConfigurationMetadataService { accountUsername: String, key: String, metadata: JsonObject - ): EntityConfigurationMetadata { + ): EntityConfigurationMetadataDTO { val account = Persistence.accountQueries.findByUsername(accountUsername).executeAsOneOrNull() ?: throw IllegalArgumentException(Constants.ACCOUNT_NOT_FOUND) @@ -20,22 +21,22 @@ class EntityConfigurationMetadataService { throw IllegalStateException(Constants.ENTITY_CONFIGURATION_METADATA_ALREADY_EXISTS) } - return Persistence.entityConfigurationMetadataQueries.create(account.id, key, metadata.toString()) - .executeAsOneOrNull() - ?: throw IllegalStateException(Constants.FAILED_TO_CREATE_ENTITY_CONFIGURATION_METADATA) - } + var createdMetadata = + Persistence.entityConfigurationMetadataQueries.create(account.id, key, metadata.toString()) + .executeAsOneOrNull() + ?: throw IllegalStateException(Constants.FAILED_TO_CREATE_ENTITY_CONFIGURATION_METADATA) - fun findByAccountId(accountId: Int): Array { - return Persistence.entityConfigurationMetadataQueries.findByAccountId(accountId).executeAsList().toTypedArray() + return createdMetadata.toEntityConfigurationMetadataDTO() } - fun findByAccountUsername(accountUsername: String): Array { + fun findByAccountUsername(accountUsername: String): Array { val account = Persistence.accountQueries.findByUsername(accountUsername).executeAsOneOrNull() ?: throw IllegalArgumentException(Constants.ACCOUNT_NOT_FOUND) - return Persistence.entityConfigurationMetadataQueries.findByAccountId(account.id).executeAsList().toTypedArray() + return Persistence.entityConfigurationMetadataQueries.findByAccountId(account.id).executeAsList() + .map { it.toEntityConfigurationMetadataDTO() }.toTypedArray() } - fun deleteEntityConfigurationMetadata(accountUsername: String, id: Int): EntityConfigurationMetadata { + fun deleteEntityConfigurationMetadata(accountUsername: String, id: Int): EntityConfigurationMetadataDTO { val account = Persistence.accountQueries.findByUsername(accountUsername).executeAsOneOrNull() ?: throw IllegalArgumentException(Constants.ACCOUNT_NOT_FOUND) @@ -47,7 +48,9 @@ class EntityConfigurationMetadataService { throw IllegalArgumentException(Constants.ENTITY_CONFIGURATION_METADATA_NOT_FOUND) } - return Persistence.entityConfigurationMetadataQueries.delete(id).executeAsOneOrNull() + val deletedMetadata = Persistence.entityConfigurationMetadataQueries.delete(id).executeAsOneOrNull() ?: throw IllegalArgumentException(Constants.ENTITY_CONFIGURATION_METADATA_NOT_FOUND) + + return deletedMetadata.toEntityConfigurationMetadataDTO() } } 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 36a4e8ad..981bd2cf 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 @@ -6,15 +6,14 @@ import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement import com.sphereon.oid.fed.openapi.models.FederationEntityMetadata import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.persistence.Persistence -import com.sphereon.oid.fed.services.extensions.toJwkDTO -import kotlinx.serialization.encodeToString +import com.sphereon.oid.fed.services.extensions.toJwkDto import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject class EntityConfigurationStatementService { private val accountService = AccountService() private val keyService = KeyService() - private val kmsService = KmsService("local") + private val kmsClient = KmsService.getKmsClient() private val entityConfigurationStatementQueries = Persistence.entityConfigurationStatementQueries private val accountQueries = Persistence.accountQueries private val subordinateQueries = Persistence.subordinateQueries @@ -24,7 +23,7 @@ class EntityConfigurationStatementService { val account = accountQueries.findByUsername(accountUsername).executeAsOneOrNull() ?: throw IllegalArgumentException(Constants.ACCOUNT_NOT_FOUND) val identifier = accountService.getAccountIdentifier(account.username) - val keys = keyService.getKeys(accountUsername).map { it.toJwkDTO() }.toTypedArray() + val keys = keyService.getKeys(accountUsername).map { it }.toTypedArray() val hasSubordinates = subordinateQueries.findByAccountId(account.id).executeAsList().isNotEmpty() val authorityHints = authorityHintQueries.findByAccountId(account.id).executeAsList().map { it.identifier }.toTypedArray() @@ -34,7 +33,7 @@ class EntityConfigurationStatementService { .iss(identifier) .iat((System.currentTimeMillis() / 1000).toInt()) .exp((System.currentTimeMillis() / 1000 + 3600 * 24 * 365).toInt()) - .jwks(keys) + .jwks(keys.map { it.toJwkDto() }.toTypedArray()) if (hasSubordinates) { val federationEntityMetadata = FederationEntityMetadataBuilder() @@ -55,33 +54,45 @@ class EntityConfigurationStatementService { metadata.forEach { entityConfigurationStatement.metadata( - Pair(it.key, Json.parseToJsonElement(it.value_).jsonObject) + Pair(it.key, Json.parseToJsonElement(it.metadata).jsonObject) ) } return entityConfigurationStatement.build() } - fun publishByUsername(accountUsername: String): EntityConfigurationStatement { + fun publishByUsername(accountUsername: String, dryRun: Boolean? = false): String { val account = accountService.getAccountByUsername(accountUsername) val entityConfigurationStatement = findByUsername(accountUsername) - val entityConfigurationStatementStr = Json.encodeToString(entityConfigurationStatement) - val entityConfigurationStatementObject = Json.parseToJsonElement(entityConfigurationStatementStr).jsonObject - val key = "key_id" - val jwt = kmsService.sign( - payload = entityConfigurationStatementObject, + val keys = keyService.getKeys(accountUsername) + + if (keys.isEmpty()) { + throw IllegalArgumentException(Constants.NO_KEYS_FOUND) + } + + val key = keys[0].kid + + val jwt = kmsClient.sign( + payload = Json.encodeToJsonElement( + EntityConfigurationStatement.serializer(), + entityConfigurationStatement + ).jsonObject, header = JWTHeader(typ = "entity-statement+jwt"), - keyId = key + keyId = key!! ) + if (dryRun == true) { + return jwt + } + entityConfigurationStatementQueries.create( account_id = account.id, expires_at = entityConfigurationStatement.exp.toLong(), statement = jwt ).executeAsOne() - return entityConfigurationStatement + return jwt } } diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt index 95ffde0f..32abaf7f 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/KeyService.kt @@ -1,58 +1,34 @@ package com.sphereon.oid.fed.services -import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair import com.sphereon.oid.fed.openapi.models.JwkAdminDTO import com.sphereon.oid.fed.persistence.Persistence -import com.sphereon.oid.fed.persistence.models.Jwk -import com.sphereon.oid.fed.services.extensions.decrypt -import com.sphereon.oid.fed.services.extensions.encrypt import com.sphereon.oid.fed.services.extensions.toJwkAdminDTO +import kotlinx.serialization.json.Json class KeyService { + private val kmsClient = KmsService.getKmsClient() private val accountQueries = Persistence.accountQueries private val keyQueries = Persistence.keyQueries - fun create(accountUsername: String): Jwk { + fun create(accountUsername: String): JwkAdminDTO { val account = accountQueries.findByUsername(accountUsername).executeAsOne() - val encryptedKeyPair = generateKeyPair().encrypt() + val jwk = kmsClient.generateKeyPair() - val key = keyQueries.create( - account.id, - y = encryptedKeyPair.y, - x = encryptedKeyPair.x, - d = encryptedKeyPair.d, - crv = encryptedKeyPair.crv, - kty = encryptedKeyPair.kty, - use = encryptedKeyPair.use, - alg = encryptedKeyPair.alg, - kid = encryptedKeyPair.kid, - e = encryptedKeyPair.e, - n = encryptedKeyPair.n, - p = encryptedKeyPair.p, - x5c = encryptedKeyPair.x5c, - dp = encryptedKeyPair.dp, - x5t_s256 = encryptedKeyPair.x5tS256, - q = encryptedKeyPair.q, - qi = encryptedKeyPair.qi, - dq = encryptedKeyPair.dq, - x5u = encryptedKeyPair.x5u, - x5t = encryptedKeyPair.x5t, + keyQueries.create( + account_id = account.id, + kid = jwk.kid!!, + key = Json.encodeToString(JwkAdminDTO.serializer(), jwk), ).executeAsOne() - return key + return jwk } - fun getDecryptedKey(keyId: Int): Jwk { - var key = keyQueries.findById(keyId).executeAsOne() - return key.decrypt() - } - - fun getKeys(accountUsername: String): Array { + fun getKeys(accountUsername: String): Array { val account = accountQueries.findByUsername(accountUsername).executeAsOne() - return keyQueries.findByAccountId(account.id).executeAsList().toTypedArray() + return keyQueries.findByAccountId(account.id).executeAsList().map { it.toJwkAdminDTO() }.toTypedArray() } fun revokeKey(accountUsername: String, keyId: Int, reason: String?): JwkAdminDTO { 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 a692c2db..5a95d04d 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 @@ -2,30 +2,22 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.openapi.models.Jwk +import com.sphereon.oid.fed.openapi.models.JwkAdminDTO import kotlinx.serialization.json.JsonObject -class KmsService(private val provider: String) { +object KmsService { + private val provider: String = System.getenv("KMS_PROVIDER") ?: "local" private val kmsClient: KmsClient = when (provider) { "local" -> LocalKmsClient() else -> throw IllegalArgumentException("Unsupported KMS provider: $provider") } - fun generateKeyPair(keyId: String) { - kmsClient.generateKeyPair(keyId) - } - - fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { - return kmsClient.sign(header, payload, keyId) - } - - fun verify(token: String, jwk: Jwk): Boolean { - return kmsClient.verify(token, jwk) - } + fun getKmsClient(): KmsClient = kmsClient } interface KmsClient { - fun generateKeyPair(keyId: String) + fun generateKeyPair(): JwkAdminDTO fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String fun verify(token: String, jwk: Jwk): Boolean -} \ No newline at end of file +} 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 b5b8c5eb..64edca2f 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,14 +3,17 @@ package com.sphereon.oid.fed.services import com.sphereon.oid.fed.kms.local.LocalKms import com.sphereon.oid.fed.openapi.models.JWTHeader import com.sphereon.oid.fed.openapi.models.Jwk +import com.sphereon.oid.fed.openapi.models.JwkAdminDTO + + import kotlinx.serialization.json.JsonObject class LocalKmsClient : KmsClient { private val localKms = LocalKms() - override fun generateKeyPair(keyId: String) { - return localKms.generateKey(keyId) + override fun generateKeyPair(): JwkAdminDTO { + return localKms.generateKey() } override fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String { @@ -20,4 +23,4 @@ class LocalKmsClient : KmsClient { override fun verify(token: String, jwk: Jwk): Boolean { return localKms.verify(token, jwk) } -} \ No newline at end of file +} diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt new file mode 100644 index 00000000..d583715e --- /dev/null +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt @@ -0,0 +1,18 @@ +package com.sphereon.oid.fed.services.extensions + +import com.sphereon.oid.fed.openapi.models.EntityConfigurationMetadataDTO +import com.sphereon.oid.fed.persistence.models.EntityConfigurationMetadata +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject + + +fun EntityConfigurationMetadata.toEntityConfigurationMetadataDTO(): EntityConfigurationMetadataDTO { + return EntityConfigurationMetadataDTO( + id = this.id, + key = this.key, + accountId = this.account_id, + metadata = Json.parseToJsonElement(this.metadata).jsonObject, + createdAt = this.created_at.toString(), + deletedAt = this.deleted_at?.toString() + ) +} diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.kt index e70fb2df..1011e0d9 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.kt @@ -3,75 +3,45 @@ package com.sphereon.oid.fed.services.extensions import com.sphereon.oid.fed.openapi.models.Jwk import com.sphereon.oid.fed.openapi.models.JwkAdminDTO import com.sphereon.oid.fed.openapi.models.JwkDTO +import kotlinx.serialization.json.Json import com.sphereon.oid.fed.persistence.models.Jwk as JwkPersistence -fun JwkPersistence.toJwkAdminDTO(): JwkAdminDTO = JwkAdminDTO( - id = id, - accountId = account_id, - uuid = uuid.toString(), - e = e, - n = n, - x = x, - y = y, - alg = alg, - crv = crv, - kid = kid, - kty = kty, - use = use, - x5c = x5c, - x5t = x5t, - x5u = x5u, - x5tHashS256 = x5t_s256, - createdAt = created_at.toString(), - revokedAt = revoked_at.toString(), - revokedReason = revoked_reason -) - -fun JwkPersistence.toJwkDTO(): JwkDTO = JwkDTO( - e = e, - n = n, - x = x, - y = y, - alg = alg, - crv = crv, - kid = kid, - kty = kty, - use = use, - x5c = x5c, - x5t = x5t, - x5u = x5u, -) - -fun Jwk.encrypt(): Jwk { - if (System.getenv("APP_KEY") == null) return this - - fun String?.encryptOrNull() = this?.let { aesEncrypt(it, System.getenv("APP_KEY")) } - - return copy( - d = d.encryptOrNull(), - dq = dq.encryptOrNull(), - qi = qi.encryptOrNull(), - dp = dp.encryptOrNull(), - p = p.encryptOrNull(), - q = q.encryptOrNull() +fun JwkPersistence.toJwkAdminDTO(): JwkAdminDTO { + val key = Json.decodeFromString(this.key) + + return JwkAdminDTO( + id = id, + accountId = account_id, + e = key.e, + x = key.x, + y = key.y, + n = key.n, + alg = key.alg, + crv = key.crv, + kid = key.kid, + kty = key.kty, + use = key.use, + x5c = key.x5c, + x5t = key.x5t, + x5u = key.x5u, + x5tHashS256 = key.x5tS256, ) } -fun JwkPersistence.decrypt(): JwkPersistence { - if (System.getenv("APP_KEY") == null) return this - - fun String?.decryptOrNull() = this?.let { aesDecrypt(it, System.getenv("APP_KEY")) } - - return copy( - d = d.decryptOrNull(), - dq = dq.decryptOrNull(), - qi = qi.decryptOrNull(), - dp = dp.decryptOrNull(), - p = p.decryptOrNull(), - q = q.decryptOrNull() +fun JwkAdminDTO.toJwkDto(): JwkDTO { + return JwkDTO( + crv = crv, + e = e, + x = x, + y = y, + n = n, + alg = alg, + kid = kid, + kty = kty!!, + use = use, + x5c = x5c, + x5t = x5t, + x5u = x5u, + x5tS256 = x5tHashS256, ) } - -expect fun aesEncrypt(data: String, key: String): String -expect fun aesDecrypt(data: String, key: String): String - diff --git a/modules/services/src/jvmMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.jvm.kt b/modules/services/src/jvmMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.jvm.kt deleted file mode 100644 index 9aa632c6..00000000 --- a/modules/services/src/jvmMain/kotlin/com/sphereon/oid/fed/services/extensions/KeyExtensions.jvm.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.sphereon.oid.fed.services.extensions - -import java.util.* -import javax.crypto.Cipher -import javax.crypto.spec.SecretKeySpec - -private const val ALGORITHM = "AES" -private const val KEY_SIZE = 32 - -actual fun aesEncrypt(data: String, key: String): String { - val secretKey = SecretKeySpec(key.padEnd(KEY_SIZE, '0').toByteArray(Charsets.UTF_8), ALGORITHM) - - val cipher = Cipher.getInstance(ALGORITHM) - cipher.init(Cipher.ENCRYPT_MODE, secretKey) - - val encryptedValue = cipher.doFinal(data.toByteArray(Charsets.UTF_8)) - return Base64.getEncoder().encodeToString(encryptedValue) -} - -actual fun aesDecrypt(data: String, key: String): String { - val secretKey = SecretKeySpec(key.padEnd(KEY_SIZE, '0').toByteArray(Charsets.UTF_8), ALGORITHM) - - val cipher = Cipher.getInstance(ALGORITHM) - cipher.init(Cipher.DECRYPT_MODE, secretKey) - - val decodedValue = Base64.getDecoder().decode(data) - val decryptedValue = cipher.doFinal(decodedValue) - return String(decryptedValue, Charsets.UTF_8) -} diff --git a/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt b/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt deleted file mode 100644 index cdb367a1..00000000 --- a/modules/services/src/jvmTest/kotlin/com/sphereon/oid/fed/services/KeyServiceTest.jvm.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.sphereon.oid.fed.services - -import com.sphereon.oid.fed.kms.local.jwk.generateKeyPair -import com.sphereon.oid.fed.services.extensions.decrypt -import com.sphereon.oid.fed.services.extensions.encrypt -import org.junit.Test -import java.time.LocalDateTime -import java.util.* -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals -import com.sphereon.oid.fed.persistence.models.Jwk as JwkPersistence - -class KeyServiceTest { - @Test - fun testEncryption() { - val key = generateKeyPair() - val encryptedKey = key.encrypt() - - if (System.getenv("APP_KEY") == null) { - assertEquals(key.d, encryptedKey.d) - } else { - assertNotEquals(key.d, encryptedKey.d) - } - - val persistenceJwk = JwkPersistence( - id = 1, - account_id = 1, - d = encryptedKey.d, - e = encryptedKey.e, - n = encryptedKey.n, - x = encryptedKey.x, - y = encryptedKey.y, - alg = encryptedKey.alg, - crv = encryptedKey.crv, - p = encryptedKey.p, - q = encryptedKey.q, - dp = encryptedKey.dp, - qi = encryptedKey.qi, - dq = encryptedKey.dq, - x5t = encryptedKey.x5t, - x5t_s256 = encryptedKey.x5tS256, - x5u = encryptedKey.x5u, - kid = encryptedKey.kid, - kty = encryptedKey.kty, - x5c = encryptedKey.x5c, - created_at = LocalDateTime.now(), - revoked_reason = null, - revoked_at = null, - uuid = UUID.randomUUID(), - use = encryptedKey.use - ) - - val decryptedPersistenceJwk = persistenceJwk.decrypt() - - assertEquals(key.d, decryptedPersistenceJwk.d) - } -} From 1464a69d2586ed3f2e303a8f822e300ea9fd45d5 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:01:35 +0200 Subject: [PATCH 19/26] fix: add missing type --- .../com/sphereon/oid/fed/openapi/openapi.yaml | 28 +++++++++++++++++++ .../EntityConfigurationMetadataService.kt | 2 +- .../EntityConfigurationMetadataExtension.kt | 3 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml index c40ef801..e2f47104 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml @@ -2510,6 +2510,34 @@ components: description: If true, the statement will not be published. example: false + EntityConfigurationMetadataDTO: + type: object + properties: + id: + type: integer + description: The metadata identifier. + example: 1 + account_id: + type: integer + description: The ID of the account associated with this entity. + example: 100 + key: + type: string + description: The metadata key. + example: openid_relying_party + metadata: + additionalProperties: true + description: The metadata object. + created_at: + type: string + format: date-time + description: The timestamp when the metadata was created. + example: 2024-08-06T12:34:56Z + required: + - key + - metadata + + CreateAuthorityHintDTO: type: object properties: diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt index 61c9261b..e902afcd 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/EntityConfigurationMetadataService.kt @@ -21,7 +21,7 @@ class EntityConfigurationMetadataService { throw IllegalStateException(Constants.ENTITY_CONFIGURATION_METADATA_ALREADY_EXISTS) } - var createdMetadata = + val createdMetadata = Persistence.entityConfigurationMetadataQueries.create(account.id, key, metadata.toString()) .executeAsOneOrNull() ?: throw IllegalStateException(Constants.FAILED_TO_CREATE_ENTITY_CONFIGURATION_METADATA) diff --git a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt index d583715e..e1ab4aec 100644 --- a/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt +++ b/modules/services/src/commonMain/kotlin/com/sphereon/oid/fed/services/extensions/EntityConfigurationMetadataExtension.kt @@ -10,9 +10,8 @@ fun EntityConfigurationMetadata.toEntityConfigurationMetadataDTO(): EntityConfig return EntityConfigurationMetadataDTO( id = this.id, key = this.key, - accountId = this.account_id, metadata = Json.parseToJsonElement(this.metadata).jsonObject, createdAt = this.created_at.toString(), - deletedAt = this.deleted_at?.toString() + accountId = this.account_id ) } From e202b8d578e4d60b7dd2fe24123ef7f70f1c67f0 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:06:57 +0200 Subject: [PATCH 20/26] fix: remove unnecessary statement --- .../oid/fed/services/EntityConfigurationStatementService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 981bd2cf..075a7ed4 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 @@ -23,7 +23,7 @@ class EntityConfigurationStatementService { val account = accountQueries.findByUsername(accountUsername).executeAsOneOrNull() ?: throw IllegalArgumentException(Constants.ACCOUNT_NOT_FOUND) val identifier = accountService.getAccountIdentifier(account.username) - val keys = keyService.getKeys(accountUsername).map { it }.toTypedArray() + val keys = keyService.getKeys(accountUsername) val hasSubordinates = subordinateQueries.findByAccountId(account.id).executeAsList().isNotEmpty() val authorityHints = authorityHintQueries.findByAccountId(account.id).executeAsList().map { it.identifier }.toTypedArray() From 8b0d25398cc93a48bd66be32468ec38930670386 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:08:34 +0200 Subject: [PATCH 21/26] fix: ci --- .github/workflows/ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92c81164..b5f70089 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,13 +18,20 @@ jobs: distribution: adopt-hotspot java-version: 17 - - name: Build the stack + - name: Run database run: docker compose -f docker-compose.yaml up db -d env: DATASOURCE_USER: ${{ secrets.DATASOURCE_USER }} DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }} + - name: Run local KMS database + run: docker compose -f docker-compose.yaml up local-kms-db -d + env: + DATASOURCE_USER: ${{ secrets.LOCAL_KMS_DATASOURCE_USER }} + DATASOURCE_PASSWORD: ${{ secrets.LOCAL_KMS_DATASOURCE_PASSWORD }} + DATASOURCE_URL: ${{ secrets.LOCAL_KMS_DATASOURCE_URL }} + - name: Setup Gradle uses: gradle/gradle-build-action@v3 From e944e88572b96d4b04eaa762c9d158a64ad60627 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:14:34 +0200 Subject: [PATCH 22/26] fix: ci --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5f70089..4a0a00a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,3 +45,7 @@ jobs: DATASOURCE_USER: ${{ secrets.DATASOURCE_USER }} DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }} + LOCAL_KMS_DATASOURCE_USER: ${{ secrets.LOCAL_KMS_DATASOURCE_USER }} + LOCAL_KMS_DATASOURCE_PASSWORD: ${{ secrets.LOCAL_KMS_DATASOURCE_PASSWORD }} + LOCAL_KMS_DATASOURCE_URL: ${{ secrets.LOCAL_KMS_DATASOURCE_URL }} + KMS_PROVIDER: local From 07da4075efab95af76f8150821435f5ce6a40f50 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:29:20 +0200 Subject: [PATCH 23/26] fix: ci --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a0a00a5..b10af5ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: - name: Execute Gradle build run: ./gradlew build env: + APP_KEY: ${{ secrets.APP_KEY }} DATASOURCE_USER: ${{ secrets.DATASOURCE_USER }} DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} DATASOURCE_URL: ${{ secrets.DATASOURCE_URL }} From 6c59d063ecb51459de5bcd5524ec234d89ab4416 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:44:18 +0200 Subject: [PATCH 24/26] fix: missing dto --- .../kotlin/com/sphereon/oid/fed/openapi/openapi.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml index ed9aa27e..4aa58c9f 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml @@ -2545,6 +2545,16 @@ components: - key - metadata + PublishEntityStatementDTO: + type: object + x-tags: + - federation + properties: + dry-run: + type: boolean + description: If true, the request will be validated but not persisted. + example: false + CreateAuthorityHintDTO: type: object properties: From f4ac52d4d09aced2e5bec70263a4385f8e632a21 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 01:49:31 +0200 Subject: [PATCH 25/26] fix: remove wrong attributes from openapi spec --- .../com/sphereon/oid/fed/openapi/openapi.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml index 4aa58c9f..5c33fdd2 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml @@ -60,19 +60,6 @@ paths: - federation summary: Fetch Entity Statement description: Fetch an Entity Statement for a specified issuer and optional subject. - parameters: - - name: iss - in: query - description: The Entity Identifier of the issuer from which the Entity Statement is issued. Because of the normalization of the URL, multiple issuers MAY resolve to a shared fetch endpoint. This parameter makes it explicit exactly which issuer the Entity Statement must come from. - required: true - schema: - type: string - - name: sub - in: query - description: The Entity Identifier of the subject for which the Entity Statement is being requested. If this parameter is omitted, it is considered to be the same as the issuer and indicates a request for a self-signed Entity Configuration. - required: false - schema: - type: string responses: '200': description: Successful fetch of Entity Statement From 4e99b341438d875c7b43be60044910242b65d868 Mon Sep 17 00:00:00 2001 From: John Melati Date: Tue, 3 Sep 2024 02:16:51 +0200 Subject: [PATCH 26/26] fix: bump openapi version --- .../commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml index 5c33fdd2..1f4af3a5 100644 --- a/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml +++ b/modules/openapi/src/commonMain/kotlin/com/sphereon/oid/fed/openapi/openapi.yaml @@ -9,7 +9,7 @@ info: license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html - version: 1.0.0-d36 + version: 1.0.0-d38 tags: - name: federation