diff --git a/src/main/kotlin/eu/europa/ec/eudi/pidissuer/port/input/IssueCredential.kt b/src/main/kotlin/eu/europa/ec/eudi/pidissuer/port/input/IssueCredential.kt index 753de116..57b00250 100644 --- a/src/main/kotlin/eu/europa/ec/eudi/pidissuer/port/input/IssueCredential.kt +++ b/src/main/kotlin/eu/europa/ec/eudi/pidissuer/port/input/IssueCredential.kt @@ -274,7 +274,6 @@ sealed interface IssueCredentialResponse { @SerialName("error") @Required val type: CredentialErrorTypeTo, @SerialName("error_description") val errorDescription: String? = null, @SerialName("c_nonce") val nonce: String? = null, - ) : IssueCredentialResponse } @@ -292,72 +291,23 @@ class IssueCredential( private val encryptCredentialResponse: EncryptCredentialResponse, ) { + private fun Raise.services(): Services = + Services(this, credentialIssuerMetadata, resolveCredentialRequestByCredentialIdentifier) + suspend operator fun invoke( authorizationContext: AuthorizationContext, credentialRequestTO: CredentialRequestTO, ): IssueCredentialResponse = coroutineScope { either { log.info("Handling issuance request for ${credentialRequestTO.format}..") - val unresolvedRequest = with(validations()) { - credentialRequestTO.toDomain( - credentialIssuerMetadata.credentialResponseEncryption, - credentialIssuerMetadata.batchCredentialIssuance, - ) - } - val (request, credentialIdentifier) = - when (unresolvedRequest) { - is UnresolvedCredentialRequest.ByFormat -> - unresolvedRequest.credentialRequest to null - - is UnresolvedCredentialRequest.ByCredentialIdentifier -> - resolve(unresolvedRequest) to unresolvedRequest.credentialIdentifier - } - val issued = issue(authorizationContext, request, credentialIdentifier) + val(request, issued) = + services().issueCredential(authorizationContext, credentialRequestTO) successResponse(request, issued) }.getOrElse { error -> errorResponse(error) } } - private suspend fun Raise.resolve( - unresolvedRequest: UnresolvedCredentialRequest.ByCredentialIdentifier, - ): CredentialRequest = - either { - val resolvedRequest = resolveCredentialRequestByCredentialIdentifier( - unresolvedRequest.credentialIdentifier, - unresolvedRequest.unvalidatedProofs, - unresolvedRequest.credentialResponseEncryption, - ) - ensureNotNull(resolvedRequest) { InvalidCredentialIdentifier(unresolvedRequest.credentialIdentifier) } - resolvedRequest - }.bind() - - private suspend fun Raise.issue( - authorizationContext: AuthorizationContext, - credentialRequest: CredentialRequest, - credentialIdentifier: CredentialIdentifier?, - ): CredentialResponse { - val issueSpecificCredential = specificIssuerFor(credentialRequest) - val expectedScope = checkNotNull(issueSpecificCredential.supportedCredential.scope) - ensure(authorizationContext.scopes.contains(expectedScope)) { WrongScope(expectedScope) } - return issueSpecificCredential(authorizationContext, credentialRequest, credentialIdentifier).bind() - } - - private fun Raise.specificIssuerFor(credentialRequest: CredentialRequest): IssueSpecificCredential { - val specificIssuer = credentialIssuerMetadata.specificCredentialIssuers - .find { issuer -> - either { assertIsSupported(credentialRequest, issuer.supportedCredential) }.isRight() - } - if (specificIssuer == null) { - val types = when (credentialRequest) { - is MsoMdocCredentialRequest -> listOf(credentialRequest.docType) - is SdJwtVcCredentialRequest -> listOf(credentialRequest.type).map { it.value } - } - raise(UnsupportedCredentialType(credentialRequest.format, types)) - } - return specificIssuer - } - private suspend fun successResponse( request: CredentialRequest, credential: CredentialResponse, @@ -378,6 +328,75 @@ class IssueCredential( return error.toTO(newCNonce) } } + +private class Services( + raise: Raise, + private val credentialIssuerMetadata: CredentialIssuerMetaData, + private val resolveCredentialRequestByCredentialIdentifier: ResolveCredentialRequestByCredentialIdentifier, +) : + Validations, + Raise by raise { + + suspend fun issueCredential( + authorizationContext: AuthorizationContext, + credentialRequestTO: CredentialRequestTO, + ): Pair = coroutineScope { + val unresolvedRequest = + credentialRequestTO.toDomain( + credentialIssuerMetadata.credentialResponseEncryption, + credentialIssuerMetadata.batchCredentialIssuance, + ) + val (request, credentialIdentifier) = + when (unresolvedRequest) { + is UnresolvedCredentialRequest.ByFormat -> + unresolvedRequest.credentialRequest to null + + is UnresolvedCredentialRequest.ByCredentialIdentifier -> + resolve(unresolvedRequest) to unresolvedRequest.credentialIdentifier + } + val issued = issue(authorizationContext, request, credentialIdentifier) + request to issued + } + + private suspend fun resolve( + unresolvedRequest: UnresolvedCredentialRequest.ByCredentialIdentifier, + ): CredentialRequest = + either { + val resolvedRequest = resolveCredentialRequestByCredentialIdentifier( + unresolvedRequest.credentialIdentifier, + unresolvedRequest.unvalidatedProofs, + unresolvedRequest.credentialResponseEncryption, + ) + ensureNotNull(resolvedRequest) { InvalidCredentialIdentifier(unresolvedRequest.credentialIdentifier) } + resolvedRequest + }.bind() + + private suspend fun issue( + authorizationContext: AuthorizationContext, + credentialRequest: CredentialRequest, + credentialIdentifier: CredentialIdentifier?, + ): CredentialResponse { + val issueSpecificCredential = specificIssuerFor(credentialRequest) + val expectedScope = checkNotNull(issueSpecificCredential.supportedCredential.scope) + ensure(authorizationContext.scopes.contains(expectedScope)) { WrongScope(expectedScope) } + return issueSpecificCredential(authorizationContext, credentialRequest, credentialIdentifier).bind() + } + + private fun specificIssuerFor(credentialRequest: CredentialRequest): IssueSpecificCredential { + val specificIssuer = credentialIssuerMetadata.specificCredentialIssuers + .find { issuer -> + either { assertIsSupported(credentialRequest, issuer.supportedCredential) }.isRight() + } + if (specificIssuer == null) { + val types = when (credentialRequest) { + is MsoMdocCredentialRequest -> listOf(credentialRequest.docType) + is SdJwtVcCredentialRequest -> listOf(credentialRequest.type).map { it.value } + } + raise(UnsupportedCredentialType(credentialRequest.format, types)) + } + return specificIssuer + } + } // // Mapping to domain // @@ -613,14 +632,7 @@ private interface Validations : Raise { UnvalidatedProof.LdpVp(ldpVp) } } - - companion object { - operator fun invoke(raise: Raise): Validations = - object : Validations, Raise by raise {} - } } -private fun Raise.validations(): Validations = - Validations(this@validations) fun CredentialResponse.toTO(cNonce: String): IssueCredentialResponse.PlainTO = when (this) { is CredentialResponse.Issued -> {