diff --git a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufParser.kt b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufParser.kt index fc54279..4db0a1a 100644 --- a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufParser.kt +++ b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufParser.kt @@ -13,8 +13,6 @@ import com.github.oslokommune.oslonokkelen.adapter.thing.ThingDescription import com.github.oslokommune.oslonokkelen.adapter.thing.ThingId import com.github.oslokommune.oslonokkelen.adapter.thing.ThingState import com.github.oslokommune.oslonokkelen.adapter.thing.ThingStateSnapshot -import com.google.gson.JsonObject -import com.google.protobuf.util.JsonFormat import com.nimbusds.jwt.JWTClaimsSet import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.persistentMapOf @@ -33,15 +31,61 @@ object ProtobufParser { fun parseActionRequestFromClaims(verifiedClaims: JWTClaimsSet) : Adapter.ActionRequest { - val requestClaim = verifiedClaims.getClaim("request") - val request = requestClaim as? JsonObject ?: throw IllegalStateException("Missing request claim") - val jsonParser = JsonFormat.parser() + val requestClaim = verifiedClaims.getJSONObjectClaim("request") val requestBuilder = Adapter.ActionRequest.newBuilder() - jsonParser.merge(request.toString(), requestBuilder) + .setRequestId(requireString(requestClaim, "requestId")) + .setThingId(requireString(requestClaim, "thingId")) + .setActionId(requireString(requestClaim, "actionId")) + .setTimeBudgetMillis(requireLong(requestClaim, "timeBudgetMillis").toInt()) + .addAllAttachments(requireList(requestClaim, "attachments").mapNotNull { attachment -> + if (attachment is Map<*, *>) { + val key = attachment.keys.firstOrNull() as? String + + when (key) { + "norwegianFodselsnummer" -> { + val value = attachment.values.firstOrNull() as Map<*, *> + + Adapter.Attachment.newBuilder() + .setNorwegianFodselsnummer(Adapter.Attachment.NorwegianFodselsnummer.newBuilder() + .setNumber(requireString(value, "number")) + .build()) + .build() + } + else -> { + null + } + } + } else { + null + } + }) return requestBuilder.build() } + private fun requireString(requestClaim: Map<*, *>, key: String): String { + val value = requestClaim[key] as? String + + return if (!value.isNullOrBlank()) { + value + } else { + throw missingKey(key, requestClaim) + } + } + + private fun requireLong(requestClaim: Map, key: String): Long { + return requestClaim[key] as? Long ?: throw missingKey(key, requestClaim) + } + + private fun requireList(requestClaim: Map, key: String): List<*> { + val value = requestClaim[key] as? List<*> + return value ?: throw missingKey(key, requestClaim) + } + + private fun missingKey(key: String,requestClaim: Map<*, *>): IllegalStateException { + return IllegalStateException("Missing key $key (found: ${requestClaim.keys.joinToString(", ")})") + } + fun parse(serializedRequest: Adapter.ActionRequest): AdapterActionRequest { return AdapterActionRequest( requestId = serializedRequest.requestId, diff --git a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGenerator.kt b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGenerator.kt index f866d68..a5f15f8 100644 --- a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGenerator.kt +++ b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGenerator.kt @@ -1,9 +1,7 @@ package com.github.oslokommune.oslonokkelen.adapter.tokens.generator import com.github.oslokommune.oslonokkelen.adapter.action.AdapterActionRequest -import com.github.oslokommune.oslonokkelen.adapter.protobuf.ProtobufSerializer -import com.google.gson.JsonParser -import com.google.protobuf.util.JsonFormat +import com.github.oslokommune.oslonokkelen.adapter.action.AdapterAttachment import com.nimbusds.jose.JWSAlgorithm import com.nimbusds.jose.JWSHeader import com.nimbusds.jose.crypto.ECDSASigner @@ -43,32 +41,40 @@ class BackendTokenGenerator( } fun createActionRequestToken(remoteUri: URI, request: AdapterActionRequest): SignedJWT { - val parsedRequest = serializeRequestAsJson(request) + val requestClaim = mapOf( + "thingId" to request.actionId.thingId.value, + "actionId" to request.actionId.value, + "timeBudgetMillis" to request.timeBudget.toMillis(), + "requestId" to request.requestId, + "attachments" to request.attachments.mapNotNull { attachment -> + when (attachment) { + is AdapterAttachment.NorwegianFodselsnummer -> { + mapOf( + "norwegianFodselsnummer" to mapOf( + "number" to attachment.number + ) + ) + } + + // These are response attachments + is AdapterAttachment.Code, + is AdapterAttachment.DeniedReason, + is AdapterAttachment.EndUserMessage, + is AdapterAttachment.ErrorDescription, + is AdapterAttachment.PunchCard -> null + } + } + ) return buildToken { audience("${remoteUri.scheme}://${remoteUri.host}") issuer("${oslonokkelenBackendUri.scheme}://${oslonokkelenBackendUri.host}") jwtID(jwtIdGenerator()) claim("scope", listOf("action:execute")) - claim("request", parsedRequest) + claim("request", requestClaim) } } - /** - * This is a bit of a hack.. - * - * We use protobuf to describe messages and it is possible to serialize the - * Java classes generated from the .proto files to json, BUT the jwt library - * can't work with these classes so we have to serialize the request to json - * and then back to classes Nimbus JWT can work with in order to embed the - * request in the token. - */ - private fun serializeRequestAsJson(request: AdapterActionRequest): Any? { - val protobufRequest = ProtobufSerializer.serialize(request) - val jsonRequest = JsonFormat.printer().print(protobufRequest) - return JsonParser.parseString(jsonRequest) - } - fun buildToken(builderBlock: JWTClaimsSet.Builder.() -> Unit): SignedJWT { val claims = createTokenClaims(builderBlock) val signingKey = tokenSigningKeySupplier.signingKeyFor() diff --git a/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGeneratorTest.kt b/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGeneratorTest.kt index 4776148..afec51a 100644 --- a/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGeneratorTest.kt +++ b/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/tokens/generator/BackendTokenGeneratorTest.kt @@ -41,6 +41,7 @@ class BackendTokenGeneratorTest { println(token.serialize()) } + companion object { private val rightNow = Instant.now()