Skip to content

Commit

Permalink
feat: add base url services to DID (#1358)
Browse files Browse the repository at this point in the history
Signed-off-by: Shota Jolbordi <[email protected]>
  • Loading branch information
shotexa authored Sep 19, 2024
1 parent 2fb5020 commit ecde01d
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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]
Expand All @@ -374,6 +387,7 @@ object ManagedDIDServiceImpl {
apollo <- ZIO.service[Apollo]
createDIDSem <- Semaphore.make(1)
} yield ManagedDIDServiceImpl(
defaultDidDocumentServices,
didService,
didOpValidator,
secretStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -22,6 +24,7 @@ class ManagedDIDServiceWithEventNotificationImpl(
createDIDSem: Semaphore,
eventNotificationService: EventNotificationService
) extends ManagedDIDServiceImpl(
defaultDidDocumentServices,
didService,
didOpValidator,
secretStorage,
Expand Down Expand Up @@ -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]
Expand All @@ -71,6 +75,7 @@ object ManagedDIDServiceWithEventNotificationImpl {
createDIDSem <- Semaphore.make(1)
eventNotificationService <- ZIO.service[EventNotificationService]
} yield ManagedDIDServiceWithEventNotificationImpl(
defaultDidDocumentServices,
didService,
didOpValidator,
secretStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit ecde01d

Please sign in to comment.