Skip to content

Commit 54b7b5b

Browse files
FabioPinheiroamagyar-iohk
andauthoredJan 13, 2025
feat: add feature flag to enable/disable support for Anoncred (backend job and API) (#1492)
Add a configuration feature flag to enable or disable support for certain type of credentials like Anomcred Related with #1491 Signed-off-by: FabioPinheiro <fabiomgpinheiro@gmail.com> Signed-off-by: Allain Magyar <allain.magyar@iohk.io> Co-authored-by: Allain Magyar <allain.magyar@iohk.io>
1 parent 37a8f41 commit 54b7b5b

File tree

13 files changed

+210
-80
lines changed

13 files changed

+210
-80
lines changed
 

Diff for: ‎build.sbt

+8-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ inThisBuild(
3737
// scalacOptions += "-Ysafe-init",
3838
// scalacOptions += "-Werror", // <=> "-Xfatal-warnings"
3939
scalacOptions += "-Dquill.macro.log=false", // disable quill macro logs // TODO https://github.com/zio/zio-protoquill/issues/470,
40-
scalacOptions ++= Seq("-Xmax-inlines", "50")
40+
scalacOptions ++= Seq("-Xmax-inlines", "50") // increase above 32 (https://github.com/circe/circe/issues/2162)
4141
)
4242
)
4343

@@ -104,15 +104,19 @@ lazy val D = new {
104104
val zioConcurrent: ModuleID = "dev.zio" %% "zio-concurrent" % V.zio
105105
val zioHttp: ModuleID = "dev.zio" %% "zio-http" % V.zioHttp
106106
val zioKafka: ModuleID = "dev.zio" %% "zio-kafka" % V.zioKafka excludeAll (
107-
ExclusionRule("dev.zio", "zio_3"), ExclusionRule("dev.zio", "zio-streams_3")
107+
ExclusionRule("dev.zio", "zio_3"),
108+
ExclusionRule("dev.zio", "zio-streams_3")
108109
)
109110
val zioCatsInterop: ModuleID = "dev.zio" %% "zio-interop-cats" % V.zioCatsInterop
110111
val zioMetricsConnectorMicrometer: ModuleID = "dev.zio" %% "zio-metrics-connectors-micrometer" % V.zioMetricsConnector
111112
val tapirPrometheusMetrics: ModuleID = "com.softwaremill.sttp.tapir" %% "tapir-prometheus-metrics" % V.tapir
112113
val micrometer: ModuleID = "io.micrometer" % "micrometer-registry-prometheus" % V.micrometer
113114
val micrometerPrometheusRegistry = "io.micrometer" % "micrometer-core" % V.micrometer
114115
val scalaUri = Seq(
115-
"io.lemonlabs" %% "scala-uri" % V.scalaUri exclude ("org.typelevel", "cats-parse_3"), // Exclude cats-parse to avoid deps conflict
116+
"io.lemonlabs" %% "scala-uri" % V.scalaUri exclude (
117+
"org.typelevel",
118+
"cats-parse_3"
119+
), // Exclude cats-parse to avoid deps conflict
116120
"org.typelevel" % "cats-parse_3" % "1.0.0", // Replace with version 1.0.0
117121
)
118122

@@ -905,7 +909,7 @@ lazy val cloudAgentServer = project
905909
Docker / maintainer := "atala-coredid@iohk.io",
906910
Docker / dockerUsername := Some("hyperledger"), // https://github.com/hyperledger
907911
Docker / dockerRepository := Some("ghcr.io"),
908-
dockerExposedPorts := Seq(8080, 8085, 8090),
912+
dockerExposedPorts := Seq(8085, 8090),
909913
// Official docker image for openjdk 21 with curl and bash
910914
dockerBaseImage := "openjdk:21-jdk",
911915
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),

Diff for: ‎cloud-agent/service/server/src/main/resources/application.conf

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ prismNode {
99
}
1010
}
1111

12+
featureFlag {
13+
enableAnoncred = false
14+
enableAnoncred = ${?ENABLE_ANONCRED}
15+
}
16+
1217
pollux {
1318
database {
1419
host = "localhost"

Diff for: ‎cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala

+6
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ object MainApp extends ZIOAppDefault {
149149
.ignore
150150

151151
appConfig <- ZIO.service[AppConfig].provide(SystemModule.configLayer)
152+
flags = appConfig.featureFlag
153+
_ <- Console.printLine(s"""### Feature Flags: ####
154+
| - Support for the credential type JWT VC is ${if (flags.enableJWT) "ENABLED" else "DISABLED"}
155+
| - Support for the credential type SD JWT VC is ${if (flags.enableSDJWT) "ENABLED" else "DISABLED"}
156+
| - Support for the credential type Anoncred is ${if (flags.enableAnoncred) "ENABLED" else "DISABLED"}
157+
|""")
152158
// these services are added to any DID document by default when they are created.
153159
defaultDidDocumentServices = Set(
154160
DidDocumentService(

Diff for: ‎cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala

+29
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.hyperledger.identus.shared.db.DbConfig
77
import org.hyperledger.identus.shared.messaging.MessagingServiceConfig
88
import zio.config.magnolia.*
99
import zio.Config
10+
import zio.ZIO
1011

1112
import java.net.URL
1213
import java.time.Duration
@@ -17,6 +18,7 @@ final case class AppConfig(
1718
agent: AgentConfig,
1819
connect: ConnectConfig,
1920
prismNode: PrismNodeConfig,
21+
featureFlag: FeatureFlagConfig
2022
) {
2123
def validate: Either[String, Unit] =
2224
for {
@@ -39,6 +41,33 @@ object AppConfig {
3941

4042
}
4143

44+
final case class FeatureFlagConfig(
45+
enableAnoncred: Boolean
46+
) {
47+
def enableJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented
48+
def enableSDJWT: Boolean = true // Hardcoded for now // TODO FeatureNotImplemented
49+
50+
def ifJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) =
51+
if (enableJWT) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForJWT)
52+
def ifSDJWTIsEnabled[R, E, A](program: ZIO[R, E, A]) =
53+
if (enableSDJWT) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForSDJWT)
54+
def ifAnoncredIsEnabled[R, E, A](program: ZIO[R, E, A]) =
55+
if (enableAnoncred) program else ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnoncred)
56+
57+
def ifJWTIsDisable[R, E, A](program: ZIO[R, E, A]) =
58+
if (!enableJWT) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForJWT) *> program else ZIO.unit
59+
def ifSDJWTIsDisable[R, E, A](program: ZIO[R, E, A]) =
60+
if (!enableSDJWT) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForSDJWT) *> program else ZIO.unit
61+
def ifAnoncredIsDisable[R, E, A](program: ZIO[R, E, A]) =
62+
if (!enableAnoncred) ZIO.logWarning(FeatureFlagConfig.messageIfDisableForAnoncred) *> program else ZIO.unit
63+
}
64+
65+
object FeatureFlagConfig {
66+
def messageIfDisableForJWT = "Feature Disabled: Credential format JWT VC"
67+
def messageIfDisableForSDJWT = "Feature Disabled: Credential format SD JWT VC"
68+
def messageIfDisableForAnoncred = "Feature Disabled: Credential format Anoncred"
69+
}
70+
4271
final case class VaultConfig(
4372
address: String,
4473
token: Option[String],

Diff for: ‎cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/IssueBackgroundJobs.scala

+82-62
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
273273
_
274274
) =>
275275
val holderPendingToGeneratedFlow = for {
276-
walletAccessContext <- ZIO
277-
.fromOption(offer.to)
278-
.mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient"))
279-
.flatMap(buildWalletAccessContextLayer)
280-
result <- for {
281-
credentialService <- ZIO.service[CredentialService]
282-
_ <- credentialService
283-
.generateJWTCredentialRequest(id)
284-
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
285-
} yield ()
286-
} yield result
276+
flags <- ZIO.service[AppConfig].map(_.featureFlag)
277+
ret <- flags.ifJWTIsEnabled(
278+
for {
279+
walletAccessContext <- ZIO
280+
.fromOption(offer.to)
281+
.mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient"))
282+
.flatMap(buildWalletAccessContextLayer)
283+
credentialService <- ZIO.service[CredentialService]
284+
_ <- credentialService
285+
.generateJWTCredentialRequest(id)
286+
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
287+
} yield ()
288+
)
289+
} yield ret
287290

288291
holderPendingToGeneratedFlow @@ HolderPendingToGeneratedSuccess.trackSuccess
289292
@@ HolderPendingToGeneratedFailed.trackError
@@ -319,18 +322,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
319322
_
320323
) =>
321324
val holderPendingToGeneratedFlow = for {
322-
walletAccessContext <- ZIO
323-
.fromOption(offer.to)
324-
.mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient"))
325-
.flatMap(buildWalletAccessContextLayer)
326-
result <- for {
327-
credentialService <- ZIO.service[CredentialService]
328-
_ <- credentialService
329-
.generateSDJWTCredentialRequest(id)
330-
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
331-
} yield ()
332-
} yield result
333-
325+
flags <- ZIO.service[AppConfig].map(_.featureFlag)
326+
ret <- flags.ifSDJWTIsEnabled(
327+
for {
328+
walletAccessContext <- ZIO
329+
.fromOption(offer.to)
330+
.mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient"))
331+
.flatMap(buildWalletAccessContextLayer)
332+
credentialService <- ZIO.service[CredentialService]
333+
_ <- credentialService
334+
.generateSDJWTCredentialRequest(id)
335+
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
336+
} yield ()
337+
)
338+
} yield ret
334339
holderPendingToGeneratedFlow @@ HolderPendingToGeneratedSuccess.trackSuccess
335340
@@ HolderPendingToGeneratedFailed.trackError
336341
@@ HolderPendingToGeneratedAll
@@ -365,19 +370,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
365370
_
366371
) =>
367372
val holderPendingToGeneratedFlow = for {
368-
walletAccessContext <- ZIO
369-
.fromOption(offer.to)
370-
.mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient"))
371-
.flatMap(buildWalletAccessContextLayer)
372-
373-
result <- for {
374-
credentialService <- ZIO.service[CredentialService]
375-
_ <- credentialService
376-
.generateAnonCredsCredentialRequest(id)
377-
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
378-
} yield ()
379-
} yield result
380-
373+
flags <- ZIO.service[AppConfig].map(_.featureFlag)
374+
ret <- flags.ifAnoncredIsEnabled(
375+
for {
376+
walletAccessContext <- ZIO
377+
.fromOption(offer.to)
378+
.mapError(_ => CredentialServiceError.CredentialOfferMissingField(id.value, "recipient"))
379+
.flatMap(buildWalletAccessContextLayer)
380+
credentialService <- ZIO.service[CredentialService]
381+
_ <- credentialService
382+
.generateAnonCredsCredentialRequest(id)
383+
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
384+
} yield ()
385+
)
386+
} yield ret
381387
holderPendingToGeneratedFlow @@ HolderPendingToGeneratedSuccess.trackSuccess
382388
@@ HolderPendingToGeneratedFailed.trackError
383389
@@ HolderPendingToGeneratedAll
@@ -517,15 +523,18 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
517523
// Set ProtocolState to CredentialGenerated
518524
// TODO Move all logic to service
519525
val issuerPendingToGeneratedFlow = for {
520-
walletAccessContext <- buildWalletAccessContextLayer(issue.from)
521-
result <- (for {
522-
credentialService <- ZIO.service[CredentialService]
523-
config <- ZIO.service[AppConfig]
524-
_ <- credentialService
525-
.generateJWTCredential(id, config.pollux.statusListRegistry.publicEndpointUrl.toExternalForm)
526-
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
527-
} yield ()).mapError(e => (walletAccessContext, e))
528-
} yield result
526+
config <- ZIO.service[AppConfig]
527+
ret <- config.featureFlag.ifJWTIsEnabled(
528+
for {
529+
walletAccessContext <- buildWalletAccessContextLayer(issue.from)
530+
credentialService <- ZIO.service[CredentialService]
531+
_ <- credentialService
532+
.generateJWTCredential(id, config.pollux.statusListRegistry.publicEndpointUrl.toExternalForm)
533+
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
534+
.mapError(e => (walletAccessContext, e))
535+
} yield ()
536+
)
537+
} yield ret
529538

530539
issuerPendingToGeneratedFlow @@ IssuerPendingToGeneratedSuccess.trackSuccess
531540
@@ IssuerPendingToGeneratedFailed.trackError
@@ -565,15 +574,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
565574
// Set ProtocolState to CredentialGenerated
566575
// TODO Move all logic to service
567576
val issuerPendingToGeneratedFlow = for {
568-
walletAccessContext <- buildWalletAccessContextLayer(issue.from)
569-
result <- (for {
570-
credentialService <- ZIO.service[CredentialService]
571-
config <- ZIO.service[AppConfig]
572-
_ <- credentialService
573-
.generateSDJWTCredential(id, config.pollux.credentialSdJwtExpirationTime)
574-
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
575-
} yield ()).mapError(e => (walletAccessContext, e))
576-
} yield result
577+
config <- ZIO.service[AppConfig]
578+
ret <- config.featureFlag.ifSDJWTIsEnabled(
579+
for {
580+
walletAccessContext <- buildWalletAccessContextLayer(issue.from)
581+
result <- (for {
582+
credentialService <- ZIO.service[CredentialService]
583+
config <- ZIO.service[AppConfig]
584+
_ <- credentialService
585+
.generateSDJWTCredential(id, config.pollux.credentialSdJwtExpirationTime)
586+
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
587+
} yield ()).mapError(e => (walletAccessContext, e))
588+
} yield result
589+
)
590+
} yield ret
577591

578592
issuerPendingToGeneratedFlow @@ IssuerPendingToGeneratedSuccess.trackSuccess
579593
@@ IssuerPendingToGeneratedFailed.trackError
@@ -609,14 +623,20 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
609623
_,
610624
) =>
611625
val issuerPendingToGeneratedFlow = for {
612-
walletAccessContext <- buildWalletAccessContextLayer(issue.from)
613-
result <- (for {
614-
credentialService <- ZIO.service[CredentialService]
615-
_ <- credentialService
616-
.generateAnonCredsCredential(id)
617-
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
618-
} yield ()).mapError(e => (walletAccessContext, e))
619-
} yield result
626+
config <- ZIO.service[AppConfig]
627+
ret <-
628+
config.featureFlag.ifAnoncredIsEnabled(
629+
for {
630+
walletAccessContext <- buildWalletAccessContextLayer(issue.from)
631+
result <- (for {
632+
credentialService <- ZIO.service[CredentialService]
633+
_ <- credentialService
634+
.generateAnonCredsCredential(id)
635+
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
636+
} yield ()).mapError(e => (walletAccessContext, e))
637+
} yield result
638+
)
639+
} yield ret
620640

621641
issuerPendingToGeneratedFlow @@ IssuerPendingToGeneratedSuccess.trackSuccess
622642
@@ IssuerPendingToGeneratedFailed.trackError

Diff for: ‎cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/PresentBackgroundJobs.scala

+21-11
Original file line numberDiff line numberDiff line change
@@ -658,16 +658,21 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
658658
requestPresentation: RequestPresentation,
659659
credentialFormat: CredentialFormat
660660
): ZIO[
661-
CredentialService & DIDService & COMMON_RESOURCES,
661+
AppConfig & CredentialService & DIDService & COMMON_RESOURCES,
662662
ERROR,
663663
Unit
664664
] = {
665-
val result =
666-
credentialFormat match {
667-
case CredentialFormat.JWT => handle_JWT_VC(id, credentialsToUse, requestPresentation)
668-
case CredentialFormat.SDJWT => handle_SD_JWT_VC(id, credentialsToUse, requestPresentation)
669-
case CredentialFormat.AnonCreds => handleAnoncred(id, maybeCredentialsToUseJson, requestPresentation)
665+
val result = for {
666+
flags <- ZIO.service[AppConfig].map(_.featureFlag)
667+
ret <- credentialFormat match {
668+
case CredentialFormat.JWT =>
669+
flags.ifJWTIsEnabled(handle_JWT_VC(id, credentialsToUse, requestPresentation))
670+
case CredentialFormat.SDJWT =>
671+
flags.ifSDJWTIsEnabled(handle_SD_JWT_VC(id, credentialsToUse, requestPresentation))
672+
case CredentialFormat.AnonCreds =>
673+
flags.ifSDJWTIsEnabled(handleAnoncred(id, maybeCredentialsToUseJson, requestPresentation))
670674
}
675+
} yield ret
671676
result @@ metric
672677
}
673678

@@ -1067,12 +1072,17 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
10671072
Failure,
10681073
Unit
10691074
] = {
1070-
val result =
1071-
credentialFormat match {
1072-
case CredentialFormat.JWT => handleJWT(id, requestPresentation, presentation, invitation)
1073-
case CredentialFormat.SDJWT => handleSDJWT(id, presentation, invitation)
1074-
case CredentialFormat.AnonCreds => handleAnoncred(id, requestPresentation, presentation, invitation)
1075+
val result = for {
1076+
flags <- ZIO.service[AppConfig].map(_.featureFlag)
1077+
ret <- credentialFormat match {
1078+
case CredentialFormat.JWT =>
1079+
flags.ifJWTIsEnabled(handleJWT(id, requestPresentation, presentation, invitation))
1080+
case CredentialFormat.SDJWT =>
1081+
flags.ifSDJWTIsEnabled(handleSDJWT(id, presentation, invitation))
1082+
case CredentialFormat.AnonCreds =>
1083+
flags.ifAnoncredIsEnabled(handleAnoncred(id, requestPresentation, presentation, invitation))
10751084
}
1085+
} yield ret
10761086
result @@ metric
10771087
}
10781088

Diff for: ‎cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala

+8
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ object ErrorResponse {
111111
detail = detail
112112
)
113113

114+
def badRequestDisabled(detail: String) =
115+
ErrorResponse(
116+
StatusCode.BadRequest.code,
117+
`type` = "BadRequest",
118+
title = "BadRequest_FeatureDisabled",
119+
detail = Some(detail)
120+
)
121+
114122
def unprocessableEntity(title: String = "UnprocessableEntity", detail: Option[String] = None) =
115123
ErrorResponse(
116124
StatusCode.UnprocessableEntity.code,

0 commit comments

Comments
 (0)
Failed to load comments.