From e1e8297a0aee81b2d78a77965db827d38b1658ad Mon Sep 17 00:00:00 2001 From: FabioPinheiro Date: Thu, 9 Jan 2025 12:04:49 +0000 Subject: [PATCH 1/5] feat: add feature flag to enable/disable support for Anomcred (backend job) Signed-off-by: FabioPinheiro --- .../src/main/resources/application.conf | 5 + .../identus/agent/server/MainApp.scala | 6 + .../agent/server/config/AppConfig.scala | 17 +++ .../server/jobs/IssueBackgroundJobs.scala | 144 ++++++++++-------- .../server/jobs/PresentBackgroundJobs.scala | 32 ++-- 5 files changed, 131 insertions(+), 73 deletions(-) diff --git a/cloud-agent/service/server/src/main/resources/application.conf b/cloud-agent/service/server/src/main/resources/application.conf index 7b5bebb01a..c608d39e86 100644 --- a/cloud-agent/service/server/src/main/resources/application.conf +++ b/cloud-agent/service/server/src/main/resources/application.conf @@ -9,6 +9,11 @@ prismNode { } } +featureFlag { + enableAnomcred = false + enableAnomcred = ${?ENABLE_ANOMCRED} +} + pollux { database { host = "localhost" diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala index a8f76d1656..2ff1be576c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala @@ -149,6 +149,12 @@ object MainApp extends ZIOAppDefault { .ignore appConfig <- ZIO.service[AppConfig].provide(SystemModule.configLayer) + flags = appConfig.featureFlag + _ <- Console.printLine(s"""### Feature Flags: #### + | - Support for the credential type JWT VC is ${if (flags.enableJWT) "ENABLED" else "DISABLED"} + | - Support for the credential type SD JWT VC is ${if (flags.enableSDJWT) "ENABLED" else "DISABLED"} + | - Support for the credential type Anomcred is ${if (flags.enableAnomcred) "ENABLED" else "DISABLED"} + |""") // these services are added to any DID document by default when they are created. defaultDidDocumentServices = Set( DidDocumentService( diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala index 364ff510bc..4ed730d4a7 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala @@ -7,6 +7,7 @@ import org.hyperledger.identus.shared.db.DbConfig import org.hyperledger.identus.shared.messaging.MessagingServiceConfig import zio.config.magnolia.* import zio.Config +import zio.ZIO import java.net.URL import java.time.Duration @@ -17,6 +18,7 @@ final case class AppConfig( agent: AgentConfig, connect: ConnectConfig, prismNode: PrismNodeConfig, + featureFlag: FeatureFlagConfig ) { def validate: Either[String, Unit] = for { @@ -39,6 +41,21 @@ object AppConfig { } +final case class FeatureFlagConfig( + enableAnomcred: Boolean +) { + def enableJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented + def enableSDJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented + + def ifJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) = + if (enableJWT) program else ZIO.logWarning("Feature Disabled: Credential format JWT VC") + def ifSDJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) = + if (enableSDJWT) program else ZIO.logWarning("Feature Disabled: Credential format SD JWT VC") + def ifAnomcredIsEnabled[R, E, A](program: ZIO[R, E, A]) = + if (enableAnomcred) program + else FeatureNotImplemented // ZIO.logWarning("Feature Disabled: Credential format Anomcred") +} + final case class VaultConfig( address: String, token: Option[String], diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala index 3cdde2853f..7ce3b30867 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala @@ -246,17 +246,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { _ ) => val holderPendingToGeneratedFlow = for { - walletAccessContext <- ZIO - .fromOption(offer.to) - .mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient")) - .flatMap(buildWalletAccessContextLayer) - result <- for { - credentialService <- ZIO.service[CredentialService] - _ <- credentialService - .generateJWTCredentialRequest(id) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - } yield () - } yield result + flags <- ZIO.service[AppConfig].map(_.featureFlag) + ret <- flags.ifJWTIsEnabled( + for { + walletAccessContext <- ZIO + .fromOption(offer.to) + .mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient")) + .flatMap(buildWalletAccessContextLayer) + credentialService <- ZIO.service[CredentialService] + _ <- credentialService + .generateJWTCredentialRequest(id) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) + } yield () + ) + } yield ret holderPendingToGeneratedFlow @@ HolderPendingToGeneratedSuccess.trackSuccess @@ HolderPendingToGeneratedFailed.trackError @@ -292,18 +295,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { _ ) => val holderPendingToGeneratedFlow = for { - walletAccessContext <- ZIO - .fromOption(offer.to) - .mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient")) - .flatMap(buildWalletAccessContextLayer) - result <- for { - credentialService <- ZIO.service[CredentialService] - _ <- credentialService - .generateSDJWTCredentialRequest(id) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - } yield () - } yield result - + flags <- ZIO.service[AppConfig].map(_.featureFlag) + ret <- flags.ifSDJWTIsEnabled( + for { + walletAccessContext <- ZIO + .fromOption(offer.to) + .mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient")) + .flatMap(buildWalletAccessContextLayer) + credentialService <- ZIO.service[CredentialService] + _ <- credentialService + .generateSDJWTCredentialRequest(id) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) + } yield () + ) + } yield ret holderPendingToGeneratedFlow @@ HolderPendingToGeneratedSuccess.trackSuccess @@ HolderPendingToGeneratedFailed.trackError @@ HolderPendingToGeneratedAll @@ -338,19 +343,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { _ ) => val holderPendingToGeneratedFlow = for { - walletAccessContext <- ZIO - .fromOption(offer.to) - .mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient")) - .flatMap(buildWalletAccessContextLayer) - - result <- for { - credentialService <- ZIO.service[CredentialService] - _ <- credentialService - .generateAnonCredsCredentialRequest(id) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - } yield () - } yield result - + flags <- ZIO.service[AppConfig].map(_.featureFlag) + ret <- flags.ifAnomcredIsEnabled( + for { + walletAccessContext <- ZIO + .fromOption(offer.to) + .mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient")) + .flatMap(buildWalletAccessContextLayer) + credentialService <- ZIO.service[CredentialService] + _ <- credentialService + .generateAnonCredsCredentialRequest(id) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) + } yield () + ) + } yield ret holderPendingToGeneratedFlow @@ HolderPendingToGeneratedSuccess.trackSuccess @@ HolderPendingToGeneratedFailed.trackError @@ HolderPendingToGeneratedAll @@ -490,15 +496,18 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { // Set ProtocolState to CredentialGenerated // TODO Move all logic to service val issuerPendingToGeneratedFlow = for { - walletAccessContext <- buildWalletAccessContextLayer(issue.from) - result <- (for { - credentialService <- ZIO.service[CredentialService] - config <- ZIO.service[AppConfig] - _ <- credentialService - .generateJWTCredential(id, config.pollux.statusListRegistry.publicEndpointUrl.toExternalForm) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - } yield ()).mapError(e => (walletAccessContext, e)) - } yield result + config <- ZIO.service[AppConfig] + ret <- config.featureFlag.ifJWTIsEnabled( + for { + walletAccessContext <- buildWalletAccessContextLayer(issue.from) + credentialService <- ZIO.service[CredentialService] + _ <- credentialService + .generateJWTCredential(id, config.pollux.statusListRegistry.publicEndpointUrl.toExternalForm) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) + .mapError(e => (walletAccessContext, e)) + } yield () + ) + } yield ret issuerPendingToGeneratedFlow @@ IssuerPendingToGeneratedSuccess.trackSuccess @@ IssuerPendingToGeneratedFailed.trackError @@ -538,15 +547,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { // Set ProtocolState to CredentialGenerated // TODO Move all logic to service val issuerPendingToGeneratedFlow = for { - walletAccessContext <- buildWalletAccessContextLayer(issue.from) - result <- (for { - credentialService <- ZIO.service[CredentialService] - config <- ZIO.service[AppConfig] - _ <- credentialService - .generateSDJWTCredential(id, config.pollux.credentialSdJwtExpirationTime) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - } yield ()).mapError(e => (walletAccessContext, e)) - } yield result + config <- ZIO.service[AppConfig] + ret <- config.featureFlag.ifSDJWTIsEnabled( + for { + walletAccessContext <- buildWalletAccessContextLayer(issue.from) + result <- (for { + credentialService <- ZIO.service[CredentialService] + config <- ZIO.service[AppConfig] + _ <- credentialService + .generateSDJWTCredential(id, config.pollux.credentialSdJwtExpirationTime) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) + } yield ()).mapError(e => (walletAccessContext, e)) + } yield result + ) + } yield ret issuerPendingToGeneratedFlow @@ IssuerPendingToGeneratedSuccess.trackSuccess @@ IssuerPendingToGeneratedFailed.trackError @@ -582,14 +596,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { _, ) => val issuerPendingToGeneratedFlow = for { - walletAccessContext <- buildWalletAccessContextLayer(issue.from) - result <- (for { - credentialService <- ZIO.service[CredentialService] - _ <- credentialService - .generateAnonCredsCredential(id) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - } yield ()).mapError(e => (walletAccessContext, e)) - } yield result + config <- ZIO.service[AppConfig] + ret <- + config.featureFlag.ifAnomcredIsEnabled( + for { + walletAccessContext <- buildWalletAccessContextLayer(issue.from) + result <- (for { + credentialService <- ZIO.service[CredentialService] + _ <- credentialService + .generateAnonCredsCredential(id) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) + } yield ()).mapError(e => (walletAccessContext, e)) + } yield result + ) + } yield ret issuerPendingToGeneratedFlow @@ IssuerPendingToGeneratedSuccess.trackSuccess @@ IssuerPendingToGeneratedFailed.trackError diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala index 4bfb247176..03b8f176fa 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala @@ -639,16 +639,21 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { requestPresentation: RequestPresentation, credentialFormat: CredentialFormat ): ZIO[ - CredentialService & DIDService & COMMON_RESOURCES, + AppConfig & CredentialService & DIDService & COMMON_RESOURCES, ERROR, Unit ] = { - val result = - credentialFormat match { - case CredentialFormat.JWT => handle_JWT_VC(id, credentialsToUse, requestPresentation) - case CredentialFormat.SDJWT => handle_SD_JWT_VC(id, credentialsToUse, requestPresentation) - case CredentialFormat.AnonCreds => handleAnoncred(id, maybeCredentialsToUseJson, requestPresentation) + val result = for { + flags <- ZIO.service[AppConfig].map(_.featureFlag) + ret <- credentialFormat match { + case CredentialFormat.JWT => + flags.ifJWTIsEnabled(handle_JWT_VC(id, credentialsToUse, requestPresentation)) + case CredentialFormat.SDJWT => + flags.ifSDJWTIsEnabled(handle_SD_JWT_VC(id, credentialsToUse, requestPresentation)) + case CredentialFormat.AnonCreds => + flags.ifSDJWTIsEnabled(handleAnoncred(id, maybeCredentialsToUseJson, requestPresentation)) } + } yield ret result @@ metric } @@ -1045,12 +1050,17 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { Failure, Unit ] = { - val result = - credentialFormat match { - case CredentialFormat.JWT => handleJWT(id, requestPresentation, presentation, invitation) - case CredentialFormat.SDJWT => handleSDJWT(id, presentation, invitation) - case CredentialFormat.AnonCreds => handleAnoncred(id, requestPresentation, presentation, invitation) + val result = for { + flags <- ZIO.service[AppConfig].map(_.featureFlag) + ret <- credentialFormat match { + case CredentialFormat.JWT => + flags.ifJWTIsEnabled(handleJWT(id, requestPresentation, presentation, invitation)) + case CredentialFormat.SDJWT => + flags.ifSDJWTIsEnabled(handleSDJWT(id, presentation, invitation)) + case CredentialFormat.AnonCreds => + flags.ifAnomcredIsEnabled(handleAnoncred(id, requestPresentation, presentation, invitation)) } + } yield ret result @@ metric } From 7d851e90eac3f534e051f6f6b4907b346092bf02 Mon Sep 17 00:00:00 2001 From: FabioPinheiro Date: Thu, 9 Jan 2025 21:06:20 +0000 Subject: [PATCH 2/5] feat: add feature flag to enable/disable support for Anomcred (API) Signed-off-by: FabioPinheiro --- .../agent/server/config/AppConfig.scala | 20 +++++++++++++---- .../identus/api/http/ErrorResponse.scala | 8 +++++++ .../controller/IssueControllerImpl.scala | 20 +++++++++++++++++ .../PresentProofControllerImpl.scala | 22 +++++++++++++++++++ .../core/model/error/PresentationError.scala | 5 +++++ 5 files changed, 71 insertions(+), 4 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala index 4ed730d4a7..ce5851664b 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala @@ -48,12 +48,24 @@ final case class FeatureFlagConfig( def enableSDJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented def ifJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) = - if (enableJWT) program else ZIO.logWarning("Feature Disabled: Credential format JWT VC") + if (enableJWT) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForJWT) def ifSDJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) = - if (enableSDJWT) program else ZIO.logWarning("Feature Disabled: Credential format SD JWT VC") + if (enableSDJWT) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForSDJWT) def ifAnomcredIsEnabled[R, E, A](program: ZIO[R, E, A]) = - if (enableAnomcred) program - else FeatureNotImplemented // ZIO.logWarning("Feature Disabled: Credential format Anomcred") + if (enableAnomcred) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnomcred) + + def ifJWTIsDisable[R, E, A](program: ZIO[R, E, A]) = + if (!enableJWT) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForJWT) *> program else ZIO.unit + def ifSDJWTIsDisable[R, E, A](program: ZIO[R, E, A]) = + if (!enableSDJWT) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForSDJWT) *> program else ZIO.unit + def ifAnomcredIsDisable[R, E, A](program: ZIO[R, E, A]) = + if (!enableAnomcred) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnomcred) *> program else ZIO.unit +} + +object FeatureFlagConfig { + def messageIfDisableForJWT = "Feature Disabled: Credential format JWT VC" + def messageIfDisableForSDJWT = "Feature Disabled: Credential format SD JWT VC" + def messageIfDisableForAnomcred = "Feature Disabled: Credential format Anomcred" } final case class VaultConfig( diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala index 219e8fa58f..314cf60c37 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala @@ -111,6 +111,14 @@ object ErrorResponse { detail = detail ) + def badRequestDisabled(detail: String) = + ErrorResponse( + StatusCode.BadRequest.code, + `type` = "BadRequest", + title = "BadRequest_FeatureDisabled", + detail = Some(detail) + ) + def unprocessableEntity(title: String = "UnprocessableEntity", detail: Option[String] = None) = ErrorResponse( StatusCode.UnprocessableEntity.code, diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index 69cfc710bf..1f955edcef 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.issue.controller import org.hyperledger.identus.agent.server.config.AppConfig +import org.hyperledger.identus.agent.server.config.FeatureFlagConfig import org.hyperledger.identus.agent.server.ControllerHelper import org.hyperledger.identus.agent.walletapi.model.PublicationState import org.hyperledger.identus.agent.walletapi.model.PublicationState.{Created, PublicationPending, Published} @@ -45,6 +46,22 @@ class IssueControllerImpl( expirationDuration: Option[Duration] ) + private def checkFeatureFlag(credentialFormat: CredentialFormat) = for { + _ <- credentialFormat match // Fail if feature is disabled + case JWT => + appConfig.featureFlag.ifJWTIsDisable( + ZIO.fail(ErrorResponse.badRequestDisabled(FeatureFlagConfig.messageIfDisableForJWT)) + ) + case SDJWT => + appConfig.featureFlag.ifSDJWTIsDisable( + ZIO.fail(ErrorResponse.badRequestDisabled(FeatureFlagConfig.messageIfDisableForSDJWT)) + ) + case AnonCreds => + appConfig.featureFlag.ifAnomcredIsDisable( + ZIO.fail(ErrorResponse.badRequestDisabled(FeatureFlagConfig.messageIfDisableForAnomcred)) + ) + } yield () + private def createCredentialOfferRecord( request: CreateIssueCredentialRecordRequest, offerContext: OfferContext @@ -59,6 +76,7 @@ class IssueControllerImpl( .fromEither(io.circe.parser.parse(request.claims.toString())) .mapError(e => ErrorResponse.badRequest(detail = Some(e.getMessage))) credentialFormat = request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT) + _ <- checkFeatureFlag(credentialFormat) outcome <- credentialFormat match case JWT => @@ -181,6 +199,7 @@ class IssueControllerImpl( connectionId <- ZIO .fromOption(request.connectionId) .mapError(_ => ErrorResponse.badRequest(detail = Some("Missing connectionId for credential offer"))) + _ <- checkFeatureFlag(request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT)) didIdPair <- getPairwiseDIDs(connectionId).provideSomeLayer(ZLayer.succeed(connectionService)) offerContext = OfferContext( pairwiseIssuerDID = didIdPair.myDID, @@ -198,6 +217,7 @@ class IssueControllerImpl( )(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, IssueCredentialRecord] = { for { peerDid <- managedDIDService.createAndStorePeerDID(appConfig.agent.didCommEndpoint.publicEndpointUrl) + _ <- checkFeatureFlag(request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT)) offerContext = OfferContext( pairwiseIssuerDID = peerDid.did, pairwiseHolderDID = None, diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala index 0d1a8ff81b..7c8d9e26f0 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.presentproof.controller import org.hyperledger.identus.agent.server.config.AppConfig +import org.hyperledger.identus.agent.server.config.FeatureFlagConfig import org.hyperledger.identus.agent.server.ControllerHelper import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.{ErrorResponse, RequestContext} @@ -11,7 +12,9 @@ import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.presentproof.{PresentCredentialRequestFormat, ProofType} import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID, PresentationRecord} import org.hyperledger.identus.pollux.core.model.error.PresentationError +import org.hyperledger.identus.pollux.core.model.error.PresentationError.UnsupportedCredentialFormatBecuaseDisabled import org.hyperledger.identus.pollux.core.model.presentation.Options +import org.hyperledger.identus.pollux.core.model.CredentialFormat.{AnonCreds, JWT, SDJWT} import org.hyperledger.identus.pollux.core.service.serdes.AnoncredPresentationRequestV1 import org.hyperledger.identus.pollux.core.service.PresentationService import org.hyperledger.identus.presentproof.controller.http.* @@ -31,11 +34,29 @@ class PresentProofControllerImpl( appConfig: AppConfig ) extends PresentProofController with ControllerHelper { + + private def checkFeatureFlag(credentialFormat: CredentialFormat) = for { + _ <- credentialFormat match // Fail if feature is disabled + case JWT => + appConfig.featureFlag.ifJWTIsDisable( + ZIO.fail(UnsupportedCredentialFormatBecuaseDisabled(FeatureFlagConfig.messageIfDisableForJWT)) + ) + case SDJWT => + appConfig.featureFlag.ifSDJWTIsDisable( + ZIO.fail(UnsupportedCredentialFormatBecuaseDisabled(FeatureFlagConfig.messageIfDisableForSDJWT)) + ) + case AnonCreds => + appConfig.featureFlag.ifAnomcredIsDisable( + ZIO.fail(UnsupportedCredentialFormatBecuaseDisabled(FeatureFlagConfig.messageIfDisableForAnomcred)) + ) + } yield () + override def requestPresentation(request: RequestPresentationInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, PresentationStatus] = { val result: ZIO[WalletAccessContext, ConnectionServiceError | PresentationError, PresentationStatus] = for { + _ <- checkFeatureFlag(request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT)) connectionId <- ZIO .fromOption(request.connectionId) .mapError(_ => PresentationError.MissingConnectionIdForPresentationRequest) @@ -54,6 +75,7 @@ class PresentProofControllerImpl( rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, PresentationStatus] = { val result: ZIO[WalletAccessContext, ConnectionServiceError | PresentationError, PresentationStatus] = for { + _ <- checkFeatureFlag(request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT)) peerDid <- managedDIDService.createAndStorePeerDID(appConfig.agent.didCommEndpoint.publicEndpointUrl) record <- createRequestPresentation( verifierDID = peerDid.did, diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala index 4f4a1d7eef..4c21459d73 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala @@ -97,6 +97,11 @@ object PresentationError { StatusCode.BadRequest, s"The Credential format '$vcFormat' is not Unsupported" ) + final case class UnsupportedCredentialFormatBecuaseDisabled(detail: String) + extends PresentationError( + StatusCode.BadRequest, + userFacingMessage = detail + ) final case class InvalidAnoncredPresentationRequest(error: String) extends PresentationError( From ad10dcb3b2703502118d610c933370e4f4587a8b Mon Sep 17 00:00:00 2001 From: FabioPinheiro Date: Fri, 10 Jan 2025 13:01:02 +0000 Subject: [PATCH 3/5] fix typos Signed-off-by: FabioPinheiro --- build.sbt | 12 ++++++++---- .../server/src/main/resources/application.conf | 4 ++-- .../hyperledger/identus/agent/server/MainApp.scala | 2 +- .../identus/agent/server/config/AppConfig.scala | 12 ++++++------ .../agent/server/jobs/IssueBackgroundJobs.scala | 4 ++-- .../agent/server/jobs/PresentBackgroundJobs.scala | 2 +- .../issue/controller/IssueControllerImpl.scala | 4 ++-- .../controller/PresentProofControllerImpl.scala | 10 +++++----- .../identus/mercury/model/Conversions.scala | 2 +- .../pollux/core/model/error/PresentationError.scala | 2 +- .../vc/jwt/VerifiablePresentationPayload.scala | 4 ++-- 11 files changed, 31 insertions(+), 27 deletions(-) diff --git a/build.sbt b/build.sbt index 5efc89ffd8..968a0cbe20 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ inThisBuild( // scalacOptions += "-Ysafe-init", // scalacOptions += "-Werror", // <=> "-Xfatal-warnings" scalacOptions += "-Dquill.macro.log=false", // disable quill macro logs // TODO https://github.com/zio/zio-protoquill/issues/470, - scalacOptions ++= Seq("-Xmax-inlines", "50") + scalacOptions ++= Seq("-Xmax-inlines", "50") // increase above 32 (https://github.com/circe/circe/issues/2162) ) ) @@ -104,7 +104,8 @@ lazy val D = new { val zioConcurrent: ModuleID = "dev.zio" %% "zio-concurrent" % V.zio val zioHttp: ModuleID = "dev.zio" %% "zio-http" % V.zioHttp val zioKafka: ModuleID = "dev.zio" %% "zio-kafka" % V.zioKafka excludeAll ( - ExclusionRule("dev.zio", "zio_3"), ExclusionRule("dev.zio", "zio-streams_3") + ExclusionRule("dev.zio", "zio_3"), + ExclusionRule("dev.zio", "zio-streams_3") ) val zioCatsInterop: ModuleID = "dev.zio" %% "zio-interop-cats" % V.zioCatsInterop val zioMetricsConnectorMicrometer: ModuleID = "dev.zio" %% "zio-metrics-connectors-micrometer" % V.zioMetricsConnector @@ -112,7 +113,10 @@ lazy val D = new { val micrometer: ModuleID = "io.micrometer" % "micrometer-registry-prometheus" % V.micrometer val micrometerPrometheusRegistry = "io.micrometer" % "micrometer-core" % V.micrometer val scalaUri = Seq( - "io.lemonlabs" %% "scala-uri" % V.scalaUri exclude ("org.typelevel", "cats-parse_3"), // Exclude cats-parse to avoid deps conflict + "io.lemonlabs" %% "scala-uri" % V.scalaUri exclude ( + "org.typelevel", + "cats-parse_3" + ), // Exclude cats-parse to avoid deps conflict "org.typelevel" % "cats-parse_3" % "1.0.0", // Replace with version 1.0.0 ) @@ -905,7 +909,7 @@ lazy val cloudAgentServer = project Docker / maintainer := "atala-coredid@iohk.io", Docker / dockerUsername := Some("hyperledger"), // https://github.com/hyperledger Docker / dockerRepository := Some("ghcr.io"), - dockerExposedPorts := Seq(8080, 8085, 8090), + dockerExposedPorts := Seq(8085, 8090), // Official docker image for openjdk 21 with curl and bash dockerBaseImage := "openjdk:21-jdk", buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), diff --git a/cloud-agent/service/server/src/main/resources/application.conf b/cloud-agent/service/server/src/main/resources/application.conf index a1dc06a3a6..d2bd4650ed 100644 --- a/cloud-agent/service/server/src/main/resources/application.conf +++ b/cloud-agent/service/server/src/main/resources/application.conf @@ -10,8 +10,8 @@ prismNode { } featureFlag { - enableAnomcred = false - enableAnomcred = ${?ENABLE_ANOMCRED} + enableAnoncred = false + enableAnocred = ${?ENABLE_ANONCRED} } pollux { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala index 2ff1be576c..6d57b988a6 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala @@ -153,7 +153,7 @@ object MainApp extends ZIOAppDefault { _ <- Console.printLine(s"""### Feature Flags: #### | - Support for the credential type JWT VC is ${if (flags.enableJWT) "ENABLED" else "DISABLED"} | - Support for the credential type SD JWT VC is ${if (flags.enableSDJWT) "ENABLED" else "DISABLED"} - | - Support for the credential type Anomcred is ${if (flags.enableAnomcred) "ENABLED" else "DISABLED"} + | - Support for the credential type Anoncred is ${if (flags.enableAnoncred) "ENABLED" else "DISABLED"} |""") // these services are added to any DID document by default when they are created. defaultDidDocumentServices = Set( diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala index 1d6c0b3723..b85ce08c08 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala @@ -42,7 +42,7 @@ object AppConfig { } final case class FeatureFlagConfig( - enableAnomcred: Boolean + enableAnoncred: Boolean ) { def enableJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented def enableSDJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented @@ -51,21 +51,21 @@ final case class FeatureFlagConfig( if (enableJWT) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForJWT) def ifSDJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) = if (enableSDJWT) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForSDJWT) - def ifAnomcredIsEnabled[R, E, A](program: ZIO[R, E, A]) = - if (enableAnomcred) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnomcred) + def ifAnoncredIsEnabled[R, E, A](program: ZIO[R, E, A]) = + if (enableAnoncred) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnoncred) def ifJWTIsDisable[R, E, A](program: ZIO[R, E, A]) = if (!enableJWT) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForJWT) *> program else ZIO.unit def ifSDJWTIsDisable[R, E, A](program: ZIO[R, E, A]) = if (!enableSDJWT) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForSDJWT) *> program else ZIO.unit - def ifAnomcredIsDisable[R, E, A](program: ZIO[R, E, A]) = - if (!enableAnomcred) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnomcred) *> program else ZIO.unit + def ifAnoncredIsDisable[R, E, A](program: ZIO[R, E, A]) = + if (!enableAnoncred) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnoncred) *> program else ZIO.unit } object FeatureFlagConfig { def messageIfDisableForJWT = "Feature Disabled: Credential format JWT VC" def messageIfDisableForSDJWT = "Feature Disabled: Credential format SD JWT VC" - def messageIfDisableForAnomcred = "Feature Disabled: Credential format Anomcred" + def messageIfDisableForAnoncred = "Feature Disabled: Credential format Anoncred" } final case class VaultConfig( diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala index e793ca8257..4f32b271a5 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala @@ -371,7 +371,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { ) => val holderPendingToGeneratedFlow = for { flags <- ZIO.service[AppConfig].map(_.featureFlag) - ret <- flags.ifAnomcredIsEnabled( + ret <- flags.ifAnoncredIsEnabled( for { walletAccessContext <- ZIO .fromOption(offer.to) @@ -625,7 +625,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper { val issuerPendingToGeneratedFlow = for { config <- ZIO.service[AppConfig] ret <- - config.featureFlag.ifAnomcredIsEnabled( + config.featureFlag.ifAnoncredIsEnabled( for { walletAccessContext <- buildWalletAccessContextLayer(issue.from) result <- (for { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala index cdac9336cb..df2458cf3f 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala @@ -1080,7 +1080,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { case CredentialFormat.SDJWT => flags.ifSDJWTIsEnabled(handleSDJWT(id, presentation, invitation)) case CredentialFormat.AnonCreds => - flags.ifAnomcredIsEnabled(handleAnoncred(id, requestPresentation, presentation, invitation)) + flags.ifAnoncredIsEnabled(handleAnoncred(id, requestPresentation, presentation, invitation)) } } yield ret result @@ metric diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index 5138755de0..edc4d051bf 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -58,8 +58,8 @@ class IssueControllerImpl( ZIO.fail(ErrorResponse.badRequestDisabled(FeatureFlagConfig.messageIfDisableForSDJWT)) ) case AnonCreds => - appConfig.featureFlag.ifAnomcredIsDisable( - ZIO.fail(ErrorResponse.badRequestDisabled(FeatureFlagConfig.messageIfDisableForAnomcred)) + appConfig.featureFlag.ifAnoncredIsDisable( + ZIO.fail(ErrorResponse.badRequestDisabled(FeatureFlagConfig.messageIfDisableForAnoncred)) ) } yield () diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala index 7c8d9e26f0..25e2e0694a 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/PresentProofControllerImpl.scala @@ -12,7 +12,7 @@ import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.presentproof.{PresentCredentialRequestFormat, ProofType} import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID, PresentationRecord} import org.hyperledger.identus.pollux.core.model.error.PresentationError -import org.hyperledger.identus.pollux.core.model.error.PresentationError.UnsupportedCredentialFormatBecuaseDisabled +import org.hyperledger.identus.pollux.core.model.error.PresentationError.UnsupportedCredentialFormatBecauseDisabled import org.hyperledger.identus.pollux.core.model.presentation.Options import org.hyperledger.identus.pollux.core.model.CredentialFormat.{AnonCreds, JWT, SDJWT} import org.hyperledger.identus.pollux.core.service.serdes.AnoncredPresentationRequestV1 @@ -39,15 +39,15 @@ class PresentProofControllerImpl( _ <- credentialFormat match // Fail if feature is disabled case JWT => appConfig.featureFlag.ifJWTIsDisable( - ZIO.fail(UnsupportedCredentialFormatBecuaseDisabled(FeatureFlagConfig.messageIfDisableForJWT)) + ZIO.fail(UnsupportedCredentialFormatBecauseDisabled(FeatureFlagConfig.messageIfDisableForJWT)) ) case SDJWT => appConfig.featureFlag.ifSDJWTIsDisable( - ZIO.fail(UnsupportedCredentialFormatBecuaseDisabled(FeatureFlagConfig.messageIfDisableForSDJWT)) + ZIO.fail(UnsupportedCredentialFormatBecauseDisabled(FeatureFlagConfig.messageIfDisableForSDJWT)) ) case AnonCreds => - appConfig.featureFlag.ifAnomcredIsDisable( - ZIO.fail(UnsupportedCredentialFormatBecuaseDisabled(FeatureFlagConfig.messageIfDisableForAnomcred)) + appConfig.featureFlag.ifAnoncredIsDisable( + ZIO.fail(UnsupportedCredentialFormatBecauseDisabled(FeatureFlagConfig.messageIfDisableForAnoncred)) ) } yield () diff --git a/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/model/Conversions.scala b/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/model/Conversions.scala index 754d19711d..a12b992791 100644 --- a/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/model/Conversions.scala +++ b/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/model/Conversions.scala @@ -37,7 +37,7 @@ given Conversion[Message, org.didcommx.didcomm.message.Message] with { msg.pleaseAck.foreach { seq => // https://identity.foundation/didcomm-messaging/spec/#acks aux.pleaseAck(true) // NOTE lib limitation the field pleaseAck MUST be a Array of string } - msg.ack.flatMap(_.headOption).foreach(str => aux.ack(str)) // NOTE: headOption becuase DidCommx only support one ack + msg.ack.flatMap(_.headOption).foreach(str => aux.ack(str)) // NOTE: headOption because DidCommx only support one ack msg.thid.foreach(str => aux.thid(str)) msg.pthid.foreach(str => aux.pthid(str)) aux.build() diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala index f1fabecd50..3ce80a10f4 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala @@ -97,7 +97,7 @@ object PresentationError { StatusCode.BadRequest, s"The Credential format '$vcFormat' is not Unsupported" ) - final case class UnsupportedCredentialFormatBecuaseDisabled(detail: String) + final case class UnsupportedCredentialFormatBecauseDisabled(detail: String) extends PresentationError( StatusCode.BadRequest, userFacingMessage = detail diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiablePresentationPayload.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiablePresentationPayload.scala index f08496efd2..84986c2880 100644 --- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiablePresentationPayload.scala +++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiablePresentationPayload.scala @@ -270,12 +270,12 @@ object JwtPresentationPayload { } //FIXME THIS WILL NOT WORK like that -case class AnomcredVp( +case class AnoncredVp( `@context`: IndexedSeq[String], `type`: IndexedSeq[String], verifiableCredential: IndexedSeq[VerifiableCredentialPayload] ) -case class AnomcredPresentationPayload( +case class AnoncredPresentationPayload( iss: String, vp: JwtVp, maybeNbf: Option[Instant], From a9ef7309946b458579c6da88ff50eaf2466c73ff Mon Sep 17 00:00:00 2001 From: FabioPinheiro Date: Fri, 10 Jan 2025 16:18:06 +0000 Subject: [PATCH 4/5] set ENABLE_ANONCRED to true in integration-test Signed-off-by: FabioPinheiro --- tests/integration-tests/src/test/resources/containers/agent.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration-tests/src/test/resources/containers/agent.yml b/tests/integration-tests/src/test/resources/containers/agent.yml index f2363cd58c..e639230e0d 100644 --- a/tests/integration-tests/src/test/resources/containers/agent.yml +++ b/tests/integration-tests/src/test/resources/containers/agent.yml @@ -57,6 +57,7 @@ services: KEYCLOAK_UMA_AUTO_UPGRADE_RPT: true # no configurable at the moment # Kafka Messaging Service DEFAULT_KAFKA_ENABLED: true + ENABLE_ANONCRED: true depends_on: postgres: condition: service_healthy From df147584b62ec911e5fbb5f019c07b1220101f01 Mon Sep 17 00:00:00 2001 From: Allain Magyar Date: Mon, 13 Jan 2025 12:00:53 -0300 Subject: [PATCH 5/5] fix: typo in configuration Signed-off-by: Allain Magyar --- cloud-agent/service/server/src/main/resources/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloud-agent/service/server/src/main/resources/application.conf b/cloud-agent/service/server/src/main/resources/application.conf index d2bd4650ed..a9a01086db 100644 --- a/cloud-agent/service/server/src/main/resources/application.conf +++ b/cloud-agent/service/server/src/main/resources/application.conf @@ -11,7 +11,7 @@ prismNode { featureFlag { enableAnoncred = false - enableAnocred = ${?ENABLE_ANONCRED} + enableAnoncred = ${?ENABLE_ANONCRED} } pollux {