Skip to content

Commit

Permalink
Removed deprecated context receivers (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
babisRoutis authored Nov 25, 2024
1 parent acb9562 commit 9771f9f
Show file tree
Hide file tree
Showing 26 changed files with 548 additions and 489 deletions.
2 changes: 0 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ kotlin {

compilerOptions {
apiVersion = KotlinVersion.KOTLIN_2_0

freeCompilerArgs.add("-Xcontext-receivers")
freeCompilerArgs.add("-Xjsr305=strict")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.input.web

import arrow.core.getOrElse
import arrow.core.raise.either
import eu.europa.ec.eudi.pidissuer.domain.CredentialConfigurationId
import eu.europa.ec.eudi.pidissuer.port.input.CreateCredentialsOffer
import eu.europa.ec.eudi.pidissuer.port.input.CreateCredentialsOfferError
Expand Down Expand Up @@ -46,18 +44,16 @@ class IssuerApi(
.map(::CredentialConfigurationId)
.toSet()

return either {
val credentialsOffer = createCredentialsOffer(credentialIds)
log.info("Successfully generated Credentials Offer. URI: '{}'", credentialsOffer)

ServerResponse.ok()
.json()
.bodyValueAndAwait(CreateCredentialsOfferResponseTO.success(credentialsOffer))
}.getOrElse { error ->
ServerResponse.badRequest()
.json()
.bodyValueAndAwait(CreateCredentialsOfferResponseTO.error(error))
}
return createCredentialsOffer(credentialIds).fold(
ifRight = { credentialsOffer ->
ServerResponse.ok().json()
.bodyValueAndAwait(CreateCredentialsOfferResponseTO.success(credentialsOffer))
.also { log.info("Successfully generated Credentials Offer. URI: '{}'", credentialsOffer) }
},
ifLeft = { error ->
ServerResponse.badRequest().json().bodyValueAndAwait(CreateCredentialsOfferResponseTO.error(error))
},
)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package eu.europa.ec.eudi.pidissuer.adapter.input.web

import arrow.core.getOrElse
import arrow.core.raise.either
import eu.europa.ec.eudi.pidissuer.domain.CredentialConfigurationId
import eu.europa.ec.eudi.pidissuer.domain.CredentialIssuerMetaData
import eu.europa.ec.eudi.pidissuer.port.input.CreateCredentialsOffer
Expand Down Expand Up @@ -83,8 +82,7 @@ class IssuerUi(
.toSet()
val credentialsOfferUri = formData["credentialsOfferUri"]?.firstOrNull { it.isNotBlank() }

return either {
val credentialsOffer = createCredentialsOffer(credentialIds, credentialsOfferUri)
return createCredentialsOffer(credentialIds, credentialsOfferUri).map { credentialsOffer ->
log.info("Successfully generated Credentials Offer. URI: '{}'", credentialsOffer)

val qrCode =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package eu.europa.ec.eudi.pidissuer.adapter.input.web

import arrow.core.NonEmptySet
import arrow.core.getOrElse
import arrow.core.raise.either
import arrow.core.raise.ensure
import arrow.core.raise.ensureNotNull
import arrow.core.raise.result
Expand Down Expand Up @@ -87,23 +85,25 @@ class WalletApi(

private suspend fun handleGetDeferredCredential(req: ServerRequest): ServerResponse = coroutineScope {
val requestTO = req.awaitBody<DeferredCredentialRequestTO>()
either {
when (val deferredResponse = getDeferredCredential(requestTO)) {
is DeferredCredentialSuccessResponse.EncryptedJwtIssued ->
ServerResponse
.ok()
.contentType(APPLICATION_JWT)
.bodyValueAndAwait(deferredResponse.jwt)

is DeferredCredentialSuccessResponse.PlainTO ->
ServerResponse
.ok()
.json()
.bodyValueAndAwait(deferredResponse)
}
}.getOrElse { error ->
ServerResponse.badRequest().json().bodyValueAndAwait(error)
}
getDeferredCredential(requestTO)
.fold(
ifRight = { deferredResponse ->
when (deferredResponse) {
is DeferredCredentialSuccessResponse.EncryptedJwtIssued ->
ServerResponse
.ok()
.contentType(APPLICATION_JWT)
.bodyValueAndAwait(deferredResponse.jwt)

is DeferredCredentialSuccessResponse.PlainTO ->
ServerResponse
.ok()
.json()
.bodyValueAndAwait(deferredResponse)
}
},
ifLeft = { error -> ServerResponse.badRequest().json().bodyValueAndAwait(error) },
)
}

private suspend fun handleHelloHolder(req: ServerRequest): ServerResponse = coroutineScope {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ import arrow.core.raise.Raise
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jose.jwk.JWK

/**
* Converts this [JWK] to an [ECKey] or raises [onFailure].
*/
context(Raise<E>)
internal fun <E> JWK.toECKeyOrFail(onFailure: () -> E): ECKey =
when (this) {
is ECKey -> this
else -> raise(onFailure())
}
internal interface JWKExtensions<in Error> : Raise<Error> {
/**
* Converts this [JWK] to an [ECKey] or raises [onFailure].
*/
fun JWK.toECKeyOrFail(onFailure: () -> Error): ECKey =
when (this) {
is ECKey -> this
else -> raise(onFailure())
}
}

internal fun <Error> Raise<Error>.jwkExtensions(): JWKExtensions<Error> =
object : JWKExtensions<Error>, Raise<Error> by this {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.jose

import arrow.core.Either
import arrow.core.NonEmptySet
import arrow.core.raise.Raise
import arrow.core.raise.either
import arrow.core.raise.ensureNotNull
import arrow.core.raise.result
import com.nimbusds.jose.JOSEObjectType
import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.JWSHeader
Expand Down Expand Up @@ -49,33 +49,33 @@ import kotlin.time.DurationUnit
internal class ValidateJwtProof(
private val credentialIssuerId: CredentialIssuerId,
) {
context(Raise<IssueCredentialError.InvalidProof>)
operator fun invoke(
unvalidatedProof: UnvalidatedProof.Jwt,
credentialConfiguration: CredentialConfiguration,
): Pair<CredentialKey, String?> {
): Either<IssueCredentialError.InvalidProof, Pair<CredentialKey, String?>> = either {
val proofType = credentialConfiguration.proofTypesSupported[ProofTypeEnum.JWT]
ensureNotNull(proofType) {
IssueCredentialError.InvalidProof("credential configuration '${credentialConfiguration.id.value}' doesn't support 'jwt' proofs")
}
check(proofType is ProofType.Jwt)

return credentialKeyAndNonce(unvalidatedProof, proofType)
credentialKeyAndNonce(unvalidatedProof, proofType).bind()
}

context(Raise<IssueCredentialError.InvalidProof>)
private fun credentialKeyAndNonce(
unvalidatedProof: UnvalidatedProof.Jwt,
proofType: ProofType.Jwt,
): Pair<CredentialKey, String?> = result {
): Either<IssueCredentialError.InvalidProof, Pair<CredentialKey, String?>> = Either.catch {
val signedJwt = SignedJWT.parse(unvalidatedProof.jwt)
val (algorithm, credentialKey) = algorithmAndCredentialKey(signedJwt.header, proofType.signingAlgorithmsSupported)
val (algorithm, credentialKey) = algorithmAndCredentialKey(
signedJwt.header,
proofType.signingAlgorithmsSupported,
)
val keySelector = keySelector(credentialKey, algorithm)
val processor = processor(credentialIssuerId, keySelector)
val claimSet = processor.process(signedJwt, null)

credentialKey to claimSet.getStringClaim("nonce")
}.getOrElse { raise(IssueCredentialError.InvalidProof("Invalid proof JWT", it)) }
}.mapLeft { IssueCredentialError.InvalidProof("Invalid proof JWT", it) }
}

private fun algorithmAndCredentialKey(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.jose

import arrow.core.Either
import arrow.core.NonEmptyList
import arrow.core.raise.Raise
import arrow.core.raise.either
import arrow.core.raise.ensure
import arrow.core.toNonEmptyListOrNull
import com.nimbusds.jose.jwk.JWK
import eu.europa.ec.eudi.pidissuer.domain.CredentialConfiguration
import eu.europa.ec.eudi.pidissuer.domain.UnvalidatedProof
import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError.InvalidProof
import eu.europa.ec.eudi.pidissuer.port.out.credential.VerifyCNonce
import kotlinx.coroutines.*
import kotlinx.coroutines.coroutineScope
import java.time.Instant

/**
Expand All @@ -36,34 +37,32 @@ internal class ValidateProofs(
private val extractJwkFromCredentialKey: ExtractJwkFromCredentialKey,
) {

context(Raise<InvalidProof>)
suspend operator fun invoke(
unvalidatedProofs: NonEmptyList<UnvalidatedProof>,
credentialConfiguration: CredentialConfiguration,
at: Instant,
): NonEmptyList<JWK> = coroutineScope {
val credentialKeysAndCNonces = unvalidatedProofs.map {
when (it) {
is UnvalidatedProof.Jwt -> async {
withContext(Dispatchers.Default) {
validateJwtProof(it, credentialConfiguration)
}
): Either<InvalidProof, NonEmptyList<JWK>> = coroutineScope {
either {
val credentialKeysAndCNonces = unvalidatedProofs.map {
when (it) {
is UnvalidatedProof.Jwt ->
validateJwtProof(it, credentialConfiguration).bind()
is UnvalidatedProof.LdpVp -> raise(InvalidProof("Supporting only JWT proof"))
}
is UnvalidatedProof.LdpVp -> raise(InvalidProof("Supporting only JWT proof"))
}
}.awaitAll()

val cnonces = credentialKeysAndCNonces.map { it.second }.toNonEmptyListOrNull()
checkNotNull(cnonces)
ensure(verifyCNonce(cnonces, at)) {
InvalidProof("CNonce is not valid")
}

val jwks = credentialKeysAndCNonces.map {
extractJwkFromCredentialKey(it.first).getOrElse { error ->
raise(InvalidProof("Unable to extract JWK from CredentialKey", error))
val cnonces = credentialKeysAndCNonces.map { it.second }.toNonEmptyListOrNull()
checkNotNull(cnonces)
ensure(verifyCNonce(cnonces, at)) {
InvalidProof("CNonce is not valid")
}
}.toNonEmptyListOrNull()
checkNotNull(jwks)

val jwks = credentialKeysAndCNonces.map {
extractJwkFromCredentialKey(it.first).getOrElse { error ->
raise(InvalidProof("Unable to extract JWK from CredentialKey", error))
}
}.toNonEmptyListOrNull()
checkNotNull(jwks)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.mdl

import arrow.core.raise.Raise
import arrow.core.Either
import com.nimbusds.jose.jwk.ECKey
import eu.europa.ec.eudi.pidissuer.adapter.out.IssuerSigningKey
import eu.europa.ec.eudi.pidissuer.adapter.out.mdl.DrivingPrivilege.Restriction.GenericRestriction
Expand Down Expand Up @@ -45,13 +45,8 @@ class DefaultEncodeMobileDrivingLicenceInCbor(
addItemsToSign(licence)
}

context(Raise<Unexpected>)
override suspend fun invoke(licence: MobileDrivingLicence, holderKey: ECKey): String =
try {
signer.sign(licence, holderKey)
} catch (t: Throwable) {
raise(Unexpected("Failed to encode mDL", t))
}
override suspend fun invoke(licence: MobileDrivingLicence, holderKey: ECKey): Either<Unexpected, String> =
Either.catch { signer.sign(licence, holderKey) }.mapLeft { Unexpected("Failed to encode mDL", it) }
}

private fun MDocBuilder.addItemsToSign(licence: MobileDrivingLicence) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.mdl

import arrow.core.raise.Raise
import arrow.core.Either
import com.nimbusds.jose.jwk.ECKey
import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError

Expand All @@ -24,6 +24,5 @@ import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError
*/
fun interface EncodeMobileDrivingLicenceInCbor {

context(Raise<IssueCredentialError.Unexpected>)
suspend operator fun invoke(licence: MobileDrivingLicence, holderKey: ECKey): String
suspend operator fun invoke(licence: MobileDrivingLicence, holderKey: ECKey): Either<IssueCredentialError.Unexpected, String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.mdl

import arrow.core.raise.Raise
import arrow.core.Either
import eu.europa.ec.eudi.pidissuer.port.input.AuthorizationContext
import eu.europa.ec.eudi.pidissuer.port.input.IssueCredentialError

Expand All @@ -28,6 +28,6 @@ fun interface GetMobileDrivingLicenceData {
* Gets the data of the Mobile Driving Licence of an authorized user. In case the authorized user
* has no Driving Licence, null is returned.
*/
context(Raise<IssueCredentialError.Unexpected>)
suspend operator fun invoke(context: AuthorizationContext): MobileDrivingLicence?

suspend operator fun invoke(context: AuthorizationContext): Either<IssueCredentialError.Unexpected, MobileDrivingLicence?>
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*/
package eu.europa.ec.eudi.pidissuer.adapter.out.mdl

import arrow.core.Either
import arrow.core.nonEmptySetOf
import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import arrow.core.raise.ensureNotNull
import eu.europa.ec.eudi.pidissuer.adapter.out.mdl.DrivingPrivilege.Restriction.GenericRestriction
import eu.europa.ec.eudi.pidissuer.adapter.out.mdl.DrivingPrivilege.Restriction.ParameterizedRestriction
Expand All @@ -34,8 +36,7 @@ import java.time.Month
*/
class GetMobileDrivingLicenceDataMock : GetMobileDrivingLicenceData {

context(Raise<IssueCredentialError.Unexpected>)
override suspend fun invoke(context: AuthorizationContext): MobileDrivingLicence {
override suspend fun invoke(context: AuthorizationContext): Either<IssueCredentialError.Unexpected, MobileDrivingLicence> = either {
log.info("Getting mock data for Mobile Driving Licence")

val driver = Driver(
Expand Down Expand Up @@ -80,7 +81,7 @@ class GetMobileDrivingLicenceDataMock : GetMobileDrivingLicenceData {
),
)

return MobileDrivingLicence(
MobileDrivingLicence(
driver,
IssueAndExpiry(LocalDate.of(2000, Month.JANUARY, 1), LocalDate.of(2040, Month.DECEMBER, 31)),
issuer,
Expand All @@ -94,8 +95,7 @@ class GetMobileDrivingLicenceDataMock : GetMobileDrivingLicenceData {

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

context(Raise<IssueCredentialError.Unexpected>)
private suspend fun loadResource(path: String): ByteArray =
private suspend fun Raise<IssueCredentialError.Unexpected>.loadResource(path: String): ByteArray =
withContext(Dispatchers.IO) {
val portrait =
ensureNotNull(Companion::class.java.getResourceAsStream(path)) {
Expand Down
Loading

0 comments on commit 9771f9f

Please sign in to comment.