Skip to content

Commit

Permalink
feat: create Entity Configuration Statement JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
jcmelati committed Sep 2, 2024
1 parent e9147e7 commit 0bd7594
Show file tree
Hide file tree
Showing 31 changed files with 327 additions and 333 deletions.
13 changes: 11 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: .
Expand Down Expand Up @@ -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

Expand All @@ -61,3 +88,4 @@ networks:

volumes:
postgres_data:
local_kms_data:
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -13,27 +19,27 @@ class EntityConfigurationMetadataController {
@GetMapping
fun get(
@PathVariable accountUsername: String
): Array<EntityConfigurationMetadata> {
): Array<EntityConfigurationMetadataDTO> {
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
)
}

@DeleteMapping("/{id}")
fun delete(
@PathVariable accountUsername: String,
@PathVariable id: Int
): EntityConfigurationMetadata {
): EntityConfigurationMetadataDTO {
return entityConfigurationMetadataService.deleteEntityConfigurationMetadata(accountUsername, id)
}
}
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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<JwkAdminDTO> {
fun getKeys(@PathVariable accountUsername: String): Array<JwkAdminDTO> {
val keys = keyService.getKeys(accountUsername)
return keys.map { it.toJwkAdminDTO() }
return keys
}

@DeleteMapping("/{keyId}")
Expand All @@ -30,4 +35,4 @@ class KeyController {
): JwkAdminDTO {
return keyService.revokeKey(accountUsername, keyId, reason)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
class KeyNotFoundException(message: String) : Exception(message)
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ package com.sphereon.oid.fed.kms.local.jwk
import com.sphereon.oid.fed.openapi.models.Jwk

expect fun generateKeyPair(): Jwk

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CREATE TABLE Keys (
id TEXT PRIMARY KEY,
private_key TEXT NOT NULL,
key TEXT NOT NULL,
deleted_at TIMESTAMP
);
);
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ?;
UPDATE Keys SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?;
Loading

0 comments on commit 0bd7594

Please sign in to comment.