diff --git a/build.sbt b/build.sbt index 802a5b7f..f1bf438d 100644 --- a/build.sbt +++ b/build.sbt @@ -50,6 +50,7 @@ lazy val D = new { val scalaDID = Def.setting("app.fmgp" %%% "did" % V.scalaDID) val scalaDID_imp = Def.setting("app.fmgp" %%% "did-imp" % V.scalaDID) val scalaDID_peer = Def.setting("app.fmgp" %%% "did-method-peer" % V.scalaDID) + val scalaDID_prism = Def.setting("app.fmgp" %%% "did-method-prism" % V.scalaDID) val scalaDID_framework = Def.setting("app.fmgp" %%% "did-framework" % V.scalaDID) val scalaDID_protocols = Def.setting("app.fmgp" %%% "did-comm-protocols" % V.scalaDID) @@ -207,6 +208,7 @@ lazy val mediator = project .settings( libraryDependencies += D.scalaDID_imp.value, libraryDependencies += D.scalaDID_peer.value, + libraryDependencies += D.scalaDID_prism.value, libraryDependencies += D.scalaDID_framework.value, libraryDependencies += D.scalaDID_protocols.value, libraryDependencies += D.zioHttp.value, @@ -272,7 +274,7 @@ lazy val webapp = project .settings( libraryDependencies ++= Seq(D.laminar.value, D.waypoint.value, D.upickle.value), libraryDependencies ++= Seq(D.zio.value, D.zioJson.value), - libraryDependencies ++= Seq(D.scalaDID.value, D.scalaDID_peer.value, D.scalaDID_protocols.value), + libraryDependencies ++= Seq(D.scalaDID.value, D.scalaDID_protocols.value), Compile / npmDependencies ++= NPM.qrcode ++ NPM.materialDesign ++ NPM.sha256, ) .settings( diff --git a/mediator/src/main/resources/application.conf b/mediator/src/main/resources/application.conf index 12b16e31..7b2dd1eb 100644 --- a/mediator/src/main/resources/application.conf +++ b/mediator/src/main/resources/application.conf @@ -1,39 +1,91 @@ mediator = { identity = { - keyAgreement = { - kty = "OKP" - crv = "X25519" - d = ${?KEY_AGREEMENT_D} - x = ${?KEY_AGREEMENT_X} - } - keyAuthentication = { - kty = "OKP" - crv = "Ed25519" - d = ${?KEY_AUTHENTICATION_D} - x = ${?KEY_AUTHENTICATION_X} - } - endpoints = "http://localhost:8080;ws://localhost:8080/ws" - endpoints = ${?SERVICE_ENDPOINTS} + did = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19" + test = { + kty = "OKP", + crv = "X25519", + d = "Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c", + x = "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw", + kid = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#key-1" + } + keyStore = [ + { # keyAgreement + kty = "OKP", + crv = "X25519", + d = ${?KEY_AGREEMENT_D}, + d = "Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c", + x = ${?KEY_AGREEMENT_X}, + x = "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw", + kid = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#key-1" + } + { # keyAuthentication + kty = "OKP", + crv = "Ed25519", + d = ${?KEY_AUTHENTICATION_D}, + d = "INXCnxFEl0atLIIQYruHzGd5sUivMRyQOzu87qVerug", + x = "MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA", + x = ${?KEY_AUTHENTICATION_X}, + kid = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#key-2" + } + ] } server.http.port = 8080 server.http.port = ${?PORT} - database = { - # Connection string takes precedence over individual components if provided - connectionString = ${?MONGODB_CONNECTION_STRING} - # Individual components (fallback if no connection string provided) - protocol = mongodb - protocol = ${?MONGODB_PROTOCOL} - port = 27017 - port = ${?MONGODB_PORT} - host = "localhost" - host = ${?MONGODB_HOST} - userName = "admin" - userName = ${?MONGODB_USER} - password = "admin" - password = ${?MONGODB_PASSWORD} - dbName = "mediator" - dbName = ${?MONGODB_DB_NAME} - } + database.connectionString = "mongodb://admin:admin@localhost:27017/mediator" + database.connectionString = ${?MONGODB_CONNECTION_STRING} problem.report.escalateTo = "atala@iohk.io" problem.report.escalateTo = ${?ESCALATE_TO} + didPrismResolver = "https://raw.githubusercontent.com/FabioPinheiro/prism-vdr/refs/heads/main/mainnet/diddoc" + didPrismResolver = ${?DID_PRISM_RESOLVER} } + + +# { +# "id" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19", +# "authentication" : [ +# { +# "id" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#key-2", +# "controller" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19", +# "type" : "JsonWebKey2020", +# "publicKeyJwk" : { +# "kty" : "OKP", +# "crv" : "Ed25519", +# "x" : "MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA" +# } +# } +# ], +# "keyAgreement" : [ +# { +# "id" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#key-1", +# "controller" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19", +# "type" : "JsonWebKey2020", +# "publicKeyJwk" : { +# "kty" : "OKP", +# "crv" : "X25519", +# "x" : "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw" +# } +# } +# ], +# "service" : [ +# { +# "id" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#service", +# "type" : "DIDCommMessaging", +# "serviceEndpoint" : { +# "uri" : "http://localhost:8080", +# "accept" : [ +# "didcomm/v2" +# ] +# } +# }, +# { +# "id" : "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImEiOlsiZGlkY29tbS92MiJdfX0.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzOi8vbG9jYWxob3N0OjgwODAvd3MiLCJhIjpbImRpZGNvbW0vdjIiXX19#service-1", +# "type" : "DIDCommMessaging", +# "serviceEndpoint" : { +# "uri" : "ws://localhost:8080/ws", +# "accept" : [ +# "didcomm/v2" +# ] +# } +# } +# ] +# } \ No newline at end of file diff --git a/mediator/src/main/scala/org/hyperledger/identus/mediator/MediatorStandalone.scala b/mediator/src/main/scala/org/hyperledger/identus/mediator/MediatorStandalone.scala index 1a2129e7..71f94b34 100644 --- a/mediator/src/main/scala/org/hyperledger/identus/mediator/MediatorStandalone.scala +++ b/mediator/src/main/scala/org/hyperledger/identus/mediator/MediatorStandalone.scala @@ -9,6 +9,7 @@ import fmgp.did.comm.* import fmgp.did.comm.protocol.* import fmgp.did.framework.TransportFactoryImp import fmgp.did.method.peer.* +import fmgp.did.method.prism.* import org.hyperledger.identus.mediator.db.* import org.hyperledger.identus.mediator.protocols.* import zio.* @@ -49,44 +50,51 @@ object CurveConfig: import CurveConfig.given case class MediatorConfig( - endpoints: String, - keyAgreement: OKPPrivateKeyWithoutKid, - keyAuthentication: OKPPrivateKeyWithoutKid + did: DIDSubject, + keyStore: KeyStore ) { - val did = DIDPeer2.makeAgent( - Seq(keyAgreement, keyAuthentication), - endpoints - .split(";") - .toSeq - .map { endpoint => fmgp.util.Base64.encode(s"""{"t":"dm","s":{"uri":"$endpoint","a":["didcomm/v2"]}}""") } - .map(DIDPeerServiceEncodedNew(_)) - ) val agentLayer: ZLayer[Any, Nothing, MediatorAgent] = - ZLayer(MediatorAgent.make(id = did.id, keyStore = did.keyStore)) + ZLayer(MediatorAgent.make(id = did, keyStore = keyStore)) +} +object MediatorConfig { + // val configPrivateKeyWithKid: Config[PrivateKeyWithKid] = { // hack to drop the nested name + // val tmp = Config.derived[PrivateKeyWithKid] // ECPrivateKeyWithKid OKPPrivateKeyWithKid + // tmp + // .asInstanceOf[Config.Fallback[PrivateKeyWithKid]] + // .first + // .asInstanceOf[Config.Lazy[PrivateKeyWithKid]] + // .thunk() + // .asInstanceOf[Config.Nested[PrivateKeyWithKid]] + // .config + // } + + val config = { + Config + .string("did") + .mapOrFail(str => + DIDSubject.either(str) match + case Left(value) => Left(Config.Error.InvalidData(Chunk("did"), "Fail to parse the DID: " + value.error)) + case Right(value) => Right(value) + ) ++ + Config + .Sequence( + Config.Fallback[PrivateKeyWithKid](Config.derived[OKPPrivateKeyWithKid], Config.derived[ECPrivateKeyWithKid]) + ) + .map(keys => KeyStore(keys.toSet)) + .nested("keyStore") + }.map((did, keyStore) => MediatorConfig(did = did, keyStore = keyStore)) } case class DataBaseConfig( - connectionString: Option[String], - protocol: String, - host: String, - port: Option[String], - userName: String, - password: String, - dbName: String + connectionString: String, ) { - private def maybePort = port.filter(_.nonEmpty).map(":" + _).getOrElse("") - private def buildConnectionString = s"$protocol://$userName:$password@$host$maybePort/$dbName" - - // Use provided connection string if available, otherwise construct from components - val finalConnectionString = connectionString.getOrElse(buildConnectionString) - + val finalConnectionString = connectionString + // Display connection string with masked password - val displayConnectionString = connectionString match { - case Some(connStr) => connStr.replaceAll("://[^:]*:[^@]*@", "://***:***@") // Mask credentials in URI - case None => s"$protocol://$userName:******@$host$maybePort/$dbName" - } - - override def toString: String = s"""DataBaseConfig($connectionString, $protocol, $host, $port, $userName, "******", $dbName)""" + val displayConnectionString = connectionString.replaceAll("://[^:]*:[^@]*@", "://***:***@") + + override def toString: String = + s"""DataBaseConfig($displayConnectionString)""" } object MediatorStandalone extends ZIOAppDefault { @@ -111,11 +119,14 @@ object MediatorStandalone extends ZIOAppDefault { |Visit: https://github.com/hyperledger-identus/mediator""".stripMargin ) configs = ConfigProvider.fromResourcePath() - mediatorConfig <- configs.nested("identity").nested("mediator").load(deriveConfig[MediatorConfig]) + mediatorConfig <- configs + .nested("identity") + .nested("mediator") + .load(MediatorConfig.config) // deriveConfig[MediatorConfig] agentLayer = mediatorConfig.agentLayer _ <- ZIO.log(s"Identus Mediator APP. See https://github.com/hyperledger-identus/mediator") _ <- ZIO.log(s"MediatorConfig: $mediatorConfig") - _ <- ZIO.log(s"DID: ${mediatorConfig.did.id.string}") + _ <- ZIO.log(s"DID: ${mediatorConfig.did.string}") mediatorDbConfig <- configs.nested("database").nested("mediator").load(deriveConfig[DataBaseConfig]) _ <- ZIO.log(s"MediatorDb Connection String: ${mediatorDbConfig.displayConnectionString}") port <- configs @@ -130,12 +141,20 @@ object MediatorStandalone extends ZIOAppDefault { .nested("mediator") .load(Config.string("escalateTo")) _ <- ZIO.log(s"Problem reports escalated to: $escalateTo") - transportFactory = Scope.default >>> (Client.default >>> TransportFactoryImp.layer) + httpClient = Scope.default ++ Client.default + transportFactory = httpClient >>> TransportFactoryImp.layer mongo = AsyncDriverResource.layer >>> ReactiveMongoApi.layer(mediatorDbConfig.finalConnectionString) repos = mongo >>> (MessageItemRepo.layer ++ UserAccountRepo.layer ++ OutboxMessageRepo.layer) + baseUrlForDIDPrismResolverVar <- configs + .nested("mediator") + .load(Config.string("didPrismResolver")) + didResolver = for { + peer <- DidPeerResolver.layerDidPeerResolver + prism <- DIDPrismResolver.layerDIDPrismResolver(baseUrlForDIDPrismResolverVar) + } yield ZEnvironment(MultiFallbackResolver(peer.get, prism.get): Resolver) // MultiParResolver(peer, prism) myServer <- Server .serve((MediatorAgent.didCommApp ++ DIDCommRoutes.app) @@ (Middleware.cors)) - .provideSomeLayer(DidPeerResolver.layerDidPeerResolver) + .provideSomeLayer(httpClient >>> HttpUtils.layer >>> didResolver) .provideSomeLayer(agentLayer) .provideSomeLayer(repos) .provideSomeLayer(Scope.default >>> ((agentLayer ++ transportFactory ++ repos) >>> OperatorImp.layer)) diff --git a/webapp/src/main/scala/org/hyperledger/identus/mediator/Global.scala b/webapp/src/main/scala/org/hyperledger/identus/mediator/Global.scala index 4a7563ee..6d9e8099 100644 --- a/webapp/src/main/scala/org/hyperledger/identus/mediator/Global.scala +++ b/webapp/src/main/scala/org/hyperledger/identus/mediator/Global.scala @@ -4,7 +4,6 @@ import com.raquo.laminar.api.L.* import fmgp.did.* import fmgp.did.comm.* import fmgp.did.comm.TO -import fmgp.did.method.peer.DIDPeer import org.scalajs.dom import scala.scalajs.js