-
Notifications
You must be signed in to change notification settings - Fork 13
feat(mediator): add opt-in did:prism support #669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2cdd8b5
74b55ff
296c1fa
64ae3f4
18de1d9
9557c75
5eede41
73982cb
f1a7c74
7db94a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| name: PRISM Integration Tests | ||
|
|
||
| concurrency: | ||
| group: ${{ github.head_ref }}${{ github.ref }}-prism-integration-tests | ||
| cancel-in-progress: true | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: | ||
| - "main" | ||
| workflow_dispatch: | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| prism-integration: | ||
| name: Run PRISM integration tests | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| env: | ||
| MEDIATOR_PRISM_E2E_ENABLED: "true" | ||
| NEOPRISM_VERSION: "0.14.1" | ||
| NEOPRISM_BASE_URL: "http://127.0.0.1:18081" | ||
| MEDIATOR_PRISM_MONGO_URI: "mongodb://127.0.0.1:27018/messages" | ||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout mediator | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
|
|
||
| - name: Setup Java and Scala | ||
| uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c # v14 | ||
| with: | ||
| java-version: openjdk@1.17 | ||
|
|
||
| - name: Start NeoPRISM dev node | ||
| run: | | ||
| docker run --rm -d \ | ||
| --name neoprism \ | ||
| -p 18081:8080 \ | ||
| -e NPRISM_DB_URL=sqlite::memory: \ | ||
| hyperledgeridentus/identus-neoprism:${NEOPRISM_VERSION} dev | ||
|
|
||
| - name: Start MongoDB | ||
| run: | | ||
| docker run --rm -d \ | ||
| --name mediator-prism-mongo \ | ||
| -p 27018:27017 \ | ||
| mongo:7 | ||
|
|
||
| - name: Wait for NeoPRISM | ||
| run: | | ||
| for i in {1..60}; do | ||
| if curl --fail --silent "${NEOPRISM_BASE_URL}/api/_system/health" > /dev/null; then | ||
| exit 0 | ||
| fi | ||
| sleep 1 | ||
| done | ||
| echo "NeoPRISM failed to become healthy" >&2 | ||
| docker logs neoprism || true | ||
| exit 1 | ||
|
|
||
| - name: Wait for MongoDB | ||
| run: | | ||
| for i in {1..60}; do | ||
| if docker exec mediator-prism-mongo mongosh --quiet --eval 'db.runCommand({ ping: 1 }).ok' | grep -qx '1'; then | ||
| exit 0 | ||
| fi | ||
| sleep 1 | ||
| done | ||
| echo "MongoDB failed to become healthy" >&2 | ||
| docker logs mediator-prism-mongo || true | ||
| exit 1 | ||
|
|
||
| - name: Run mediator PRISM integration test | ||
| run: | | ||
| sbt -mem 2048 -J-Xmx5120m "mediator/testOnly org.hyperledger.identus.mediator.prism.MediatorPrismE2ESpec" | ||
|
|
||
| - name: Show NeoPRISM logs on failure | ||
| if: failure() | ||
| run: docker logs neoprism || true | ||
|
|
||
| - name: Show MongoDB logs on failure | ||
| if: failure() | ||
| run: docker logs mediator-prism-mongo || true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,20 +50,60 @@ object CurveConfig: | |
| import CurveConfig.given | ||
|
|
||
| case class MediatorConfig( | ||
| endpoints: String, | ||
| keyAgreement: OKPPrivateKeyWithoutKid, | ||
| keyAuthentication: OKPPrivateKeyWithoutKid | ||
| did: DID, | ||
| 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 { | ||
|
|
||
| def legacy( | ||
| endpoints: String, | ||
| keyAgreement: OKPPrivateKeyWithoutKid, | ||
| keyAuthentication: OKPPrivateKeyWithoutKid | ||
| ): MediatorConfig = { | ||
| val agent = 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(_)) | ||
| ) | ||
| MediatorConfig(did = agent.id, keyStore = agent.keyStore) | ||
| } | ||
|
|
||
| private val didConfig = | ||
| 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.toDID: DID) | ||
| ) | ||
|
|
||
| private val keyStoreConfig = | ||
| Config | ||
| .Sequence( | ||
| Config.Fallback[PrivateKeyWithKid](Config.derived[OKPPrivateKeyWithKid], Config.derived[ECPrivateKeyWithKid]) | ||
| ) | ||
| .map(keys => KeyStore(keys.toSet)) | ||
| .nested("keyStore") | ||
|
|
||
| private val explicitConfig = | ||
| (didConfig ++ keyStoreConfig).map((did, keyStore) => MediatorConfig(did = did, keyStore = keyStore)) | ||
|
|
||
| private val legacyConfig = | ||
| ( | ||
| Config.string("endpoints") ++ | ||
| Config.derived[OKPPrivateKeyWithoutKid].nested("keyAgreement") ++ | ||
| Config.derived[OKPPrivateKeyWithoutKid].nested("keyAuthentication") | ||
| ).map((endpoints, keyAgreement, keyAuthentication) => legacy(endpoints, keyAgreement, keyAuthentication)) | ||
|
|
||
| val config: Config[MediatorConfig] = | ||
| Config.Fallback(explicitConfig, legacyConfig) | ||
|
Comment on lines
+105
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an operator supplies an explicit AGENTS.md reference: AGENTS.md:L128-L130 Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| case class DataBaseConfig( | ||
|
|
@@ -99,6 +140,18 @@ object MediatorStandalone extends ZIOAppDefault { | |
| override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = | ||
| Runtime.removeDefaultLoggers >>> SLF4J.slf4j(mediatorColorFormat) | ||
|
|
||
| private def resolverLayer(didPrismResolverBaseUrl: String): ZLayer[Client & Scope, Nothing, Resolver] = | ||
| ( | ||
| DidPeerResolver.layerDidPeerResolver ++ | ||
| (HttpUtils.layer >>> DIDPrismResolver.layerDIDPrismResolver(didPrismResolverBaseUrl)) | ||
| ) >>> | ||
| ZLayer.fromZIO( | ||
| for { | ||
| peer <- ZIO.service[DidPeerResolver] | ||
| prism <- ZIO.service[DIDPrismResolver] | ||
| } yield MultiFallbackResolver(peer, prism): Resolver | ||
| ) | ||
|
|
||
| def mainProgram = for { | ||
| _ <- Console.printLine( // https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Mediator | ||
| """███╗ ███╗███████╗██████╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ | ||
|
|
@@ -111,11 +164,11 @@ 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) | ||
| 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 +183,18 @@ 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) | ||
| didPrismResolverBaseUrl <- configs | ||
| .nested("mediator") | ||
| .load(Config.string("didPrismResolver")) | ||
| _ <- ZIO.log(s"DID PRISM resolver: $didPrismResolverBaseUrl") | ||
| httpClient = Scope.default ++ Client.default | ||
| transportFactory = httpClient >>> TransportFactoryImp.layer | ||
| resolver = httpClient >>> resolverLayer(didPrismResolverBaseUrl) | ||
| mongo = AsyncDriverResource.layer >>> ReactiveMongoApi.layer(mediatorDbConfig.finalConnectionString) | ||
| repos = mongo >>> (MessageItemRepo.layer ++ UserAccountRepo.layer ++ OutboxMessageRepo.layer) | ||
| myServer <- Server | ||
| .serve((MediatorAgent.didCommApp ++ DIDCommRoutes.app) @@ (Middleware.cors)) | ||
| .provideSomeLayer(DidPeerResolver.layerDidPeerResolver) | ||
| .provideSomeLayer(resolver) | ||
| .provideSomeLayer(agentLayer) | ||
| .provideSomeLayer(repos) | ||
| .provideSomeLayer(Scope.default >>> ((agentLayer ++ transportFactory ++ repos) >>> OperatorImp.layer)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This removes
-Xfatal-warningsfrom the entire mediator test configuration merely to accommodate generated protobuf sources, so warnings in all handwritten tests will now pass locally and in CI. That bypasses the repository's documented no-warning quality gate; scope warning suppression to the managed ScalaPB output (or suppress its specific diagnostics) rather than disabling the gate for every test source.AGENTS.md reference: AGENTS.md:L265-L270
Useful? React with 👍 / 👎.