Skip to content

Commit

Permalink
Change granularity of IssuedCredential.
Browse files Browse the repository at this point in the history
  • Loading branch information
dzarras committed Sep 26, 2024
1 parent de4c82d commit 871dab1
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ fun beans(clock: Clock) = beans {
//
with(InMemoryIssuedCredentialRepository()) {
bean { GenerateNotificationId.Random }
bean { storeIssuedCredential }
bean { loadIssuedCredentialByNotificationId }
bean { storeIssuedCredentials }
bean { loadIssuedCredentialsByNotificationId }
}

//
Expand Down Expand Up @@ -389,7 +389,7 @@ fun beans(clock: Clock) = beans {
?: true,
generateNotificationId = ref(),
clock = clock,
storeIssuedCredential = ref(),
storeIssuedCredentials = ref(),
)
add(issueMsoMdocPid)
}
Expand Down Expand Up @@ -419,7 +419,7 @@ fun beans(clock: Clock) = beans {
notificationsEnabled = env.getProperty<Boolean>("issuer.pid.sd_jwt_vc.notifications.enabled")
?: true,
generateNotificationId = ref(),
storeIssuedCredential = ref(),
storeIssuedCredentials = ref(),
)

val deferred = env.getProperty<Boolean>("issuer.pid.sd_jwt_vc.deferred") ?: false
Expand All @@ -437,7 +437,7 @@ fun beans(clock: Clock) = beans {
notificationsEnabled = env.getProperty<Boolean>("issuer.mdl.notifications.enabled") ?: true,
generateNotificationId = ref(),
clock = clock,
storeIssuedCredential = ref(),
storeIssuedCredentials = ref(),
)
add(mdlIssuer)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError
import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError.InvalidProof
import eu.europa.ec.eudi.pidissuer.port.out.IssueSpecificCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.GenerateNotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredentials
import kotlinx.coroutines.*
import kotlinx.serialization.json.JsonPrimitive
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -301,7 +301,7 @@ class IssueMobileDrivingLicence(
private val notificationsEnabled: Boolean,
private val generateNotificationId: GenerateNotificationId,
private val clock: Clock,
private val storeIssuedCredential: StoreIssuedCredential,
private val storeIssuedCredentials: StoreIssuedCredentials,
) : IssueSpecificCredential {

override val supportedCredential: MsoMdocCredentialConfiguration
Expand Down Expand Up @@ -333,27 +333,29 @@ class IssueMobileDrivingLicence(
if (notificationsEnabled) generateNotificationId()
else null

val issuedCredentials = holderKeys.awaitAll().map { holderKey ->
val cbor = encodeMobileDrivingLicenceInCbor(licence, holderKey)
storeIssuedCredential(
IssuedCredential(
format = MSO_MDOC_FORMAT,
type = supportedCredential.docType,
holder = with(licence.driver) {
"${familyName.latin.value} ${givenName.latin.value}"
},
holderPublicKey = holderKey.toPublicJWK(),
issuedAt = clock.instant(),
notificationId = notificationId,
),
)
cbor
}.toNonEmptyListOrNull()
val issuedCredentials = holderKeys.awaitAll()
.map { holderKey ->
val cbor = encodeMobileDrivingLicenceInCbor(licence, holderKey)
cbor to holderKey.toPublicJWK()
}.toNonEmptyListOrNull()
ensureNotNull(issuedCredentials) {
IssueCredentialError.Unexpected("Unable to issue mDL")
}

CredentialResponse.Issued(issuedCredentials.map { JsonPrimitive(it) }, notificationId)
storeIssuedCredentials(
IssuedCredentials(
format = MSO_MDOC_FORMAT,
type = supportedCredential.docType,
holder = with(licence.driver) {
"${familyName.latin.value} ${givenName.latin.value}"
},
holderPublicKeys = issuedCredentials.map { it.second },
issuedAt = clock.instant(),
notificationId = notificationId,
),
)

CredentialResponse.Issued(issuedCredentials.map { JsonPrimitive(it.first) }, notificationId)
.also {
log.info("Successfully issued mDL(s)")
log.debug("Issued mDL(s) data {}", it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.persistence

import eu.europa.ec.eudi.pidissuer.domain.IssuedCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.LoadIssuedCredentialByNotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredential
import eu.europa.ec.eudi.pidissuer.domain.IssuedCredentials
import eu.europa.ec.eudi.pidissuer.port.out.persistence.LoadIssuedCredentialsByNotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredentials
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.slf4j.LoggerFactory

private val log = LoggerFactory.getLogger(InMemoryIssuedCredentialRepository::class.java)

class InMemoryIssuedCredentialRepository(
private val data: MutableList<IssuedCredential> = mutableListOf(),
private val data: MutableList<IssuedCredentials> = mutableListOf(),
) {
private val mutex = Mutex()

val storeIssuedCredential: StoreIssuedCredential =
StoreIssuedCredential { credential ->
val storeIssuedCredentials: StoreIssuedCredentials =
StoreIssuedCredentials { credential ->
mutex.withLock(this) {
if (credential.notificationId != null) {
require(data.find { existing -> existing.notificationId == credential.notificationId } == null) {
Expand All @@ -42,8 +42,8 @@ class InMemoryIssuedCredentialRepository(
}
}

val loadIssuedCredentialByNotificationId: LoadIssuedCredentialByNotificationId =
LoadIssuedCredentialByNotificationId { notificationId ->
val loadIssuedCredentialsByNotificationId: LoadIssuedCredentialsByNotificationId =
LoadIssuedCredentialsByNotificationId { notificationId ->
mutex.withLock(this) {
data.find { credential -> credential.notificationId == notificationId }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError
import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError.InvalidProof
import eu.europa.ec.eudi.pidissuer.port.out.IssueSpecificCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.GenerateNotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredentials
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
Expand Down Expand Up @@ -264,7 +264,7 @@ class IssueMsoMdocPid(
private val notificationsEnabled: Boolean,
private val generateNotificationId: GenerateNotificationId,
private val clock: Clock,
private val storeIssuedCredential: StoreIssuedCredential,
private val storeIssuedCredentials: StoreIssuedCredentials,
) : IssueSpecificCredential {

private val log = LoggerFactory.getLogger(IssueMsoMdocPid::class.java)
Expand Down Expand Up @@ -298,25 +298,26 @@ class IssueMsoMdocPid(
val cbor = encodePidInCbor(pid, pidMetaData, holderKey).also {
log.info("Issued $it")
}
storeIssuedCredential(
IssuedCredential(
format = MSO_MDOC_FORMAT,
type = supportedCredential.docType,
holder = with(pid) {
"${familyName.value} ${givenName.value}"
},
holderPublicKey = holderKey.toPublicJWK(),
issuedAt = clock.instant(),
notificationId = notificationId,
),
)
cbor
cbor to holderKey.toPublicJWK()
}.toNonEmptyListOrNull()
ensureNotNull(issuedCredentials) {
IssueCredentialError.Unexpected("Unable to issue PID")
}

CredentialResponse.Issued(issuedCredentials.map { JsonPrimitive(it) }, notificationId)
storeIssuedCredentials(
IssuedCredentials(
format = MSO_MDOC_FORMAT,
type = supportedCredential.docType,
holder = with(pid) {
"${familyName.value} ${givenName.value}"
},
holderPublicKeys = issuedCredentials.map { it.second },
issuedAt = clock.instant(),
notificationId = notificationId,
),
)

CredentialResponse.Issued(issuedCredentials.map { JsonPrimitive(it.first) }, notificationId)
.also {
log.info("Successfully issued PIDs")
log.debug("Issued PIDs data {}", it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError
import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError.InvalidProof
import eu.europa.ec.eudi.pidissuer.port.out.IssueSpecificCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.GenerateNotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredential
import eu.europa.ec.eudi.pidissuer.port.out.persistence.StoreIssuedCredentials
import eu.europa.ec.eudi.sdjwt.HashAlgorithm
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
Expand Down Expand Up @@ -119,7 +119,7 @@ class IssueSdJwtVcPid(
private val calculateNotUseBefore: TimeDependant<Instant>?,
private val notificationsEnabled: Boolean,
private val generateNotificationId: GenerateNotificationId,
private val storeIssuedCredential: StoreIssuedCredential,
private val storeIssuedCredentials: StoreIssuedCredentials,
) : IssueSpecificCredential {

private val validateProof = ValidateProof(credentialIssuerId)
Expand Down Expand Up @@ -159,25 +159,26 @@ class IssueSdJwtVcPid(
else null
val issuedCredentials = holderPubKeys.awaitAll().map { holderPubKey ->
val sdJwt = encodePidInSdJwt.invoke(pid, pidMetaData, holderPubKey)
storeIssuedCredential(
IssuedCredential(
format = SD_JWT_VC_FORMAT,
type = supportedCredential.type.value,
holder = with(pid) {
"${familyName.value} ${givenName.value}"
},
holderPublicKey = holderPubKey.toPublicJWK(),
issuedAt = clock.instant(),
notificationId = notificationId,
),
)
sdJwt
sdJwt to holderPubKey.toPublicJWK()
}.toNonEmptyListOrNull()
ensureNotNull(issuedCredentials) {
IssueCredentialError.Unexpected("Unable to issue PID")
}

CredentialResponse.Issued(issuedCredentials.map { JsonPrimitive(it) }, notificationId)
storeIssuedCredentials(
IssuedCredentials(
format = SD_JWT_VC_FORMAT,
type = supportedCredential.type.value,
holder = with(pid) {
"${familyName.value} ${givenName.value}"
},
holderPublicKeys = issuedCredentials.map { it.second },
issuedAt = clock.instant(),
notificationId = notificationId,
),
)

CredentialResponse.Issued(issuedCredentials.map { JsonPrimitive(it.first) }, notificationId)
.also {
log.info("Successfully issued PIDs")
log.debug("Issued PIDs data {}", it)
Expand Down
7 changes: 4 additions & 3 deletions src/main/kotlin/eu/europa/ec/eudi/pidissuer/domain/Types.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package eu.europa.ec.eudi.pidissuer.domain

import arrow.core.NonEmptyList
import com.nimbusds.jose.jwk.JWK
import org.slf4j.LoggerFactory
import java.net.MalformedURLException
Expand Down Expand Up @@ -105,13 +106,13 @@ sealed interface CryptographicBindingMethod {
}

/**
* A credential that was issued by a specific issuing service.
* Credential that have issued by a specific issuing service.
*/
data class IssuedCredential(
data class IssuedCredentials(
val format: Format,
val type: String,
val holder: String,
val holderPublicKey: JWK,
val holderPublicKeys: NonEmptyList<JWK>,
val issuedAt: Instant,
val notificationId: NotificationId? = null,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package eu.europa.ec.eudi.pidissuer.port.input

import arrow.core.Either
import eu.europa.ec.eudi.pidissuer.domain.NotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.LoadIssuedCredentialByNotificationId
import eu.europa.ec.eudi.pidissuer.port.out.persistence.LoadIssuedCredentialsByNotificationId
import kotlinx.serialization.Required
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down Expand Up @@ -93,18 +93,18 @@ private val log = LoggerFactory.getLogger(HandleNotificationRequest::class.java)
* Handles an incoming Notification Request.
*/
class HandleNotificationRequest(
private val loadIssuedCredentialByNotificationId: LoadIssuedCredentialByNotificationId,
private val loadIssuedCredentialsByNotificationId: LoadIssuedCredentialsByNotificationId,
) {
suspend operator fun invoke(requestBody: JsonElement): NotificationResponse =
Either.catch { Json.decodeFromJsonElement<NotificationRequestTO>(requestBody) }
.fold(
ifLeft = { NotificationResponse.NotificationErrorResponseTO(ErrorTypeTO.InvalidNotificationRequest) },
ifRight = { notificationRequest ->
val notificationId = NotificationId(notificationRequest.notificationId)
val credential = loadIssuedCredentialByNotificationId(notificationId)
val credentials = loadIssuedCredentialsByNotificationId(notificationId)

credential?.let {
log.info("Received Notification Request '$notificationRequest' for Credential '$it'")
credentials?.let {
log.info("Received Notification Request '$notificationRequest' for Credentials '$it'")
NotificationResponse.Success
} ?: NotificationResponse.NotificationErrorResponseTO(ErrorTypeTO.InvalidNotificationId)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
*/
package eu.europa.ec.eudi.pidissuer.port.out.persistence

import eu.europa.ec.eudi.pidissuer.domain.IssuedCredential
import eu.europa.ec.eudi.pidissuer.domain.IssuedCredentials
import eu.europa.ec.eudi.pidissuer.domain.NotificationId

fun interface LoadIssuedCredentialByNotificationId {
fun interface LoadIssuedCredentialsByNotificationId {

/**
* Loads an issued credential by its notification id.
*/
suspend operator fun invoke(notificationId: NotificationId): IssuedCredential?
suspend operator fun invoke(notificationId: NotificationId): IssuedCredentials?
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
*/
package eu.europa.ec.eudi.pidissuer.port.out.persistence

import eu.europa.ec.eudi.pidissuer.domain.IssuedCredential
import eu.europa.ec.eudi.pidissuer.domain.IssuedCredentials

fun interface StoreIssuedCredential {
fun interface StoreIssuedCredentials {

/**
* Stores an issued credential
*/
suspend operator fun invoke(credential: IssuedCredential)
suspend operator fun invoke(credentials: IssuedCredentials)
}

0 comments on commit 871dab1

Please sign in to comment.