From ecde01d90f1500bca4fb77ba73f930d8ae9cb57b Mon Sep 17 00:00:00 2001 From: shotexa Date: Thu, 19 Sep 2024 21:31:03 +0400 Subject: [PATCH] feat: add base url services to DID (#1358) Signed-off-by: Shota Jolbordi --- .../identus/agent/server/MainApp.scala | 39 +++++++++++- .../walletapi/service/ManagedDIDService.scala | 2 + .../service/ManagedDIDServiceImpl.scala | 22 +++++-- ...dDIDServiceWithEventNotificationImpl.scala | 9 ++- .../util/ManagedDIDTemplateValidator.scala | 18 +++++- .../service/ManagedDIDServiceSpec.scala | 61 ++++++++++++++----- 6 files changed, 128 insertions(+), 23 deletions(-) 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 b0309e4e94..e012215770 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 @@ -18,6 +18,11 @@ import org.hyperledger.identus.agent.walletapi.sql.{ } import org.hyperledger.identus.agent.walletapi.storage.GenericSecretStorage import org.hyperledger.identus.castor.controller.{DIDControllerImpl, DIDRegistrarControllerImpl} +import org.hyperledger.identus.castor.core.model.did.{ + Service as DidDocumentService, + ServiceEndpoint as DidDocumentServiceEndpoint, + ServiceType as DidDocumentServiceType +} import org.hyperledger.identus.castor.core.service.DIDServiceImpl import org.hyperledger.identus.castor.core.util.DIDOperationValidator import org.hyperledger.identus.connect.controller.ConnectionControllerImpl @@ -139,6 +144,38 @@ object MainApp extends ZIOAppDefault { |""".stripMargin) .ignore + appConfig <- ZIO.service[AppConfig].provide(SystemModule.configLayer) + // these services are added to any DID document by default when they are created. + defaultDidDocumentServices = Set( + DidDocumentService( + id = appConfig.agent.httpEndpoint.serviceName, + serviceEndpoint = DidDocumentServiceEndpoint + .Single( + DidDocumentServiceEndpoint.UriOrJsonEndpoint + .Uri( + DidDocumentServiceEndpoint.UriValue + .fromString(appConfig.agent.httpEndpoint.publicEndpointUrl.toString) + .toOption + .get // This will fail if URL is invalid, which will prevent app from starting since public endpoint in config is invalid + ) + ), + `type` = DidDocumentServiceType.Single(DidDocumentServiceType.Name.fromStringUnsafe("LinkedResourceV1")) + ), + DidDocumentService( + id = appConfig.pollux.statusListRegistry.serviceName, + serviceEndpoint = DidDocumentServiceEndpoint + .Single( + DidDocumentServiceEndpoint.UriOrJsonEndpoint + .Uri( + DidDocumentServiceEndpoint.UriValue + .fromString(appConfig.pollux.statusListRegistry.publicEndpointUrl.toString) + .toOption + .get + ) + ), + `type` = DidDocumentServiceType.Single(DidDocumentServiceType.Name.fromStringUnsafe("LinkedResourceV1")) + ) + ) _ <- preMigrations _ <- migrations @@ -183,7 +220,7 @@ object MainApp extends ZIOAppDefault { LinkSecretServiceImpl.layer >>> CredentialServiceImpl.layer >>> CredentialServiceNotifier.layer, DIDServiceImpl.layer, EntityServiceImpl.layer, - ManagedDIDServiceWithEventNotificationImpl.layer, + ZLayer.succeed(defaultDidDocumentServices) >>> ManagedDIDServiceWithEventNotificationImpl.layer, LinkSecretServiceImpl.layer >>> PresentationServiceImpl.layer >>> PresentationServiceNotifier.layer, VerificationPolicyServiceImpl.layer, WalletManagementServiceImpl.layer, diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDService.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDService.scala index e8cba848c1..48caa375c4 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDService.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDService.scala @@ -17,6 +17,8 @@ trait ManagedDIDService { private[walletapi] def nonSecretStorage: DIDNonSecretStorage + protected def getDefaultDidDocumentServices: Set[Service] = Set.empty + def syncManagedDIDState: ZIO[WalletAccessContext, GetManagedDIDError, Unit] def syncUnconfirmedUpdateOperations: ZIO[WalletAccessContext, GetManagedDIDError, Unit] diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceImpl.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceImpl.scala index b021c28d33..b0936fa24e 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceImpl.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceImpl.scala @@ -8,6 +8,7 @@ import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService.DEFAULT import org.hyperledger.identus.agent.walletapi.storage.{DIDNonSecretStorage, DIDSecretStorage, WalletSecretStorage} import org.hyperledger.identus.agent.walletapi.util.* import org.hyperledger.identus.castor.core.model.did.* +import org.hyperledger.identus.castor.core.model.did.Service as DidDocumentService import org.hyperledger.identus.castor.core.model.error.DIDOperationError import org.hyperledger.identus.castor.core.service.DIDService import org.hyperledger.identus.castor.core.util.DIDOperationValidator @@ -24,6 +25,7 @@ import scala.language.implicitConversions * indy-wallet-sdk. */ class ManagedDIDServiceImpl private[walletapi] ( + defaultDidDocumentServices: Set[DidDocumentService], didService: DIDService, didOpValidator: DIDOperationValidator, private[walletapi] val secretStorage: DIDSecretStorage, @@ -54,6 +56,8 @@ class ManagedDIDServiceImpl private[walletapi] ( def syncUnconfirmedUpdateOperations: ZIO[WalletAccessContext, GetManagedDIDError, Unit] = syncUnconfirmedUpdateOperationsByDID(did = None) + override protected def getDefaultDidDocumentServices: Set[DidDocumentService] = defaultDidDocumentServices + override def findDIDKeyPair( did: CanonicalPrismDID, keyId: String @@ -125,9 +129,16 @@ class ManagedDIDServiceImpl private[walletapi] ( ): ZIO[WalletAccessContext, CreateManagedDIDError, LongFormPrismDID] = { val effect = for { _ <- ZIO - .fromEither(ManagedDIDTemplateValidator.validate(didTemplate)) - .mapError(CreateManagedDIDError.InvalidArgument.apply) - material <- didCreateHandler.materialize(didTemplate) + .fromEither(ManagedDIDTemplateValidator.validate(didTemplate, defaultDidDocumentServices)) + .mapError { x => + println("x: " + x) + + CreateManagedDIDError.InvalidArgument(x) + } + _ <- ZIO.logInfo(s"Old did template after validation: $didTemplate") + newDidTemplate = didTemplate.copy(services = didTemplate.services ++ defaultDidDocumentServices) + _ <- ZIO.logInfo(s"Creating managed DID with template2: $newDidTemplate") + material <- didCreateHandler.materialize(newDidTemplate) _ <- ZIO .fromEither(didOpValidator.validate(material.operation)) .mapError(CreateManagedDIDError.InvalidOperation.apply) @@ -361,11 +372,13 @@ class ManagedDIDServiceImpl private[walletapi] ( object ManagedDIDServiceImpl { val layer: RLayer[ - DIDOperationValidator & DIDService & DIDSecretStorage & DIDNonSecretStorage & WalletSecretStorage & Apollo, + Set[DidDocumentService] & DIDOperationValidator & DIDService & DIDSecretStorage & DIDNonSecretStorage & + WalletSecretStorage & Apollo, ManagedDIDService ] = { ZLayer.fromZIO { for { + defaultDidDocumentServices <- ZIO.service[Set[DidDocumentService]] didService <- ZIO.service[DIDService] didOpValidator <- ZIO.service[DIDOperationValidator] secretStorage <- ZIO.service[DIDSecretStorage] @@ -374,6 +387,7 @@ object ManagedDIDServiceImpl { apollo <- ZIO.service[Apollo] createDIDSem <- Semaphore.make(1) } yield ManagedDIDServiceImpl( + defaultDidDocumentServices, didService, didOpValidator, secretStorage, diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceWithEventNotificationImpl.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceWithEventNotificationImpl.scala index 782ee6d9ec..6938ef3547 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceWithEventNotificationImpl.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceWithEventNotificationImpl.scala @@ -4,6 +4,7 @@ import org.hyperledger.identus.agent.walletapi.model.error.CommonWalletStorageEr import org.hyperledger.identus.agent.walletapi.model.ManagedDIDDetail import org.hyperledger.identus.agent.walletapi.storage.{DIDNonSecretStorage, DIDSecretStorage, WalletSecretStorage} import org.hyperledger.identus.castor.core.model.did.CanonicalPrismDID +import org.hyperledger.identus.castor.core.model.did.Service as DidDocumentService import org.hyperledger.identus.castor.core.model.error.DIDOperationError import org.hyperledger.identus.castor.core.service.DIDService import org.hyperledger.identus.castor.core.util.DIDOperationValidator @@ -13,6 +14,7 @@ import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* class ManagedDIDServiceWithEventNotificationImpl( + defaultDidDocumentServices: Set[DidDocumentService], didService: DIDService, didOpValidator: DIDOperationValidator, override private[walletapi] val secretStorage: DIDSecretStorage, @@ -22,6 +24,7 @@ class ManagedDIDServiceWithEventNotificationImpl( createDIDSem: Semaphore, eventNotificationService: EventNotificationService ) extends ManagedDIDServiceImpl( + defaultDidDocumentServices, didService, didOpValidator, secretStorage, @@ -57,11 +60,12 @@ class ManagedDIDServiceWithEventNotificationImpl( object ManagedDIDServiceWithEventNotificationImpl { val layer: RLayer[ - DIDOperationValidator & DIDService & DIDSecretStorage & DIDNonSecretStorage & WalletSecretStorage & Apollo & - EventNotificationService, + Set[DidDocumentService] & DIDOperationValidator & DIDService & DIDSecretStorage & DIDNonSecretStorage & + WalletSecretStorage & Apollo & EventNotificationService, ManagedDIDService ] = ZLayer.fromZIO { for { + defaultDidDocumentServices <- ZIO.service[Set[DidDocumentService]] didService <- ZIO.service[DIDService] didOpValidator <- ZIO.service[DIDOperationValidator] secretStorage <- ZIO.service[DIDSecretStorage] @@ -71,6 +75,7 @@ object ManagedDIDServiceWithEventNotificationImpl { createDIDSem <- Semaphore.make(1) eventNotificationService <- ZIO.service[EventNotificationService] } yield ManagedDIDServiceWithEventNotificationImpl( + defaultDidDocumentServices, didService, didOpValidator, secretStorage, diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/util/ManagedDIDTemplateValidator.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/util/ManagedDIDTemplateValidator.scala index 82abee2b7f..3e9385ae86 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/util/ManagedDIDTemplateValidator.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/util/ManagedDIDTemplateValidator.scala @@ -3,15 +3,31 @@ package org.hyperledger.identus.agent.walletapi.util import org.hyperledger.identus.agent.walletapi.model.ManagedDIDTemplate import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.castor.core.model.did.{EllipticCurve, VerificationRelationship} +import org.hyperledger.identus.castor.core.model.did.Service as DidDocumentService object ManagedDIDTemplateValidator { - def validate(template: ManagedDIDTemplate): Either[String, Unit] = + def validate( + template: ManagedDIDTemplate, + defaultDidDocumentServices: Set[DidDocumentService] = Set.empty + ): Either[String, Unit] = for { _ <- validateReservedKeyId(template) _ <- validateCurveUsage(template) + _ <- validatePresenceOfDefaultDidServices(template.services, defaultDidDocumentServices) } yield () + private def validatePresenceOfDefaultDidServices( + services: Seq[DidDocumentService], + defaultDidDocumentServices: Set[DidDocumentService] + ): Either[String, Unit] = { + + services.map(_.id).intersect(defaultDidDocumentServices.toSeq.map(_.id)) match { + case Nil => Right(()) + case x => Left(s"Default DID services cannot be overridden: ${x.mkString("[", ", ", "]")}") + } + } + private def validateReservedKeyId(template: ManagedDIDTemplate): Either[String, Unit] = { val keyIds = template.publicKeys.map(_.id) val reservedKeyIds = keyIds.filter(id => ManagedDIDService.reservedKeyIds.contains(id)) diff --git a/cloud-agent/service/wallet-api/src/test/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceSpec.scala b/cloud-agent/service/wallet-api/src/test/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceSpec.scala index 4ab90ff8e1..60e686a39d 100644 --- a/cloud-agent/service/wallet-api/src/test/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceSpec.scala +++ b/cloud-agent/service/wallet-api/src/test/scala/org/hyperledger/identus/agent/walletapi/service/ManagedDIDServiceSpec.scala @@ -11,6 +11,11 @@ import org.hyperledger.identus.agent.walletapi.sql.* import org.hyperledger.identus.agent.walletapi.storage.* import org.hyperledger.identus.agent.walletapi.vault.{VaultDIDSecretStorage, VaultWalletSecretStorage} import org.hyperledger.identus.castor.core.model.did.* +import org.hyperledger.identus.castor.core.model.did.{ + Service as DidDocumentService, + ServiceEndpoint as DidDocumentServiceEndpoint, + ServiceType as DidDocumentServiceType +} import org.hyperledger.identus.castor.core.model.error import org.hyperledger.identus.castor.core.service.DIDService import org.hyperledger.identus.castor.core.util.DIDOperationValidator @@ -82,21 +87,38 @@ object ManagedDIDServiceSpec ) private def serviceLayer = - ZLayer - .makeSome[ - DIDSecretStorage & WalletSecretStorage, - WalletManagementService & ManagedDIDService & TestDIDService - ]( - ManagedDIDServiceImpl.layer, - WalletManagementServiceImpl.layer, - DIDOperationValidator.layer(), - JdbcDIDNonSecretStorage.layer, - JdbcWalletNonSecretStorage.layer, - systemTransactorLayer, - contextAwareTransactorLayer, - testDIDServiceLayer, - apolloLayer - ) + ZLayer.succeed(Set(defaultDidDocumentServiceFixture)) >>> serviceLayerWithoutDidDocumentServices + + private def serviceLayerWithoutDidDocumentServices = ZLayer + .makeSome[ + Set[DidDocumentService] & DIDSecretStorage & WalletSecretStorage, + WalletManagementService & ManagedDIDService & TestDIDService + ]( + ManagedDIDServiceImpl.layer, + WalletManagementServiceImpl.layer, + DIDOperationValidator.layer(), + JdbcDIDNonSecretStorage.layer, + JdbcWalletNonSecretStorage.layer, + systemTransactorLayer, + contextAwareTransactorLayer, + testDIDServiceLayer, + apolloLayer + ) + + private val defaultDidDocumentServiceFixture = DidDocumentService( + id = "agent-base-url", + serviceEndpoint = DidDocumentServiceEndpoint + .Single( + DidDocumentServiceEndpoint.UriOrJsonEndpoint + .Uri( + DidDocumentServiceEndpoint.UriValue + .fromString("http://localhost:8085/") + .toOption + .get // This will fail if URL is invalid, which will prevent app from starting since public endpoint in config is invalid + ) + ), + `type` = DidDocumentServiceType.Single(DidDocumentServiceType.Name.fromStringUnsafe("LinkedResourceV1")) + ) private def generateDIDTemplate( publicKeys: Seq[DIDPublicKeyTemplate] = Nil, @@ -234,6 +256,15 @@ object ManagedDIDServiceSpec } yield assert(didsBefore)(isEmpty) && assert(didsAfter.map(_._1))(hasSameElements(Seq(did))) }, + test("will not create a DID if one of the provided servies includes default service") { + val template = generateDIDTemplate( + services = Seq( + defaultDidDocumentServiceFixture + ) + ) + val result = ZIO.serviceWithZIO[ManagedDIDService](_.createAndStoreDID(template)) + assertZIO(result.exit)(fails(isSubtype[CreateManagedDIDError.InvalidArgument](anything))) + }, test("create and store DID secret in DIDSecretStorage") { val template = generateDIDTemplate( publicKeys = Seq(