Skip to content

Commit

Permalink
feat(agent): integrate createManagedDID in HTTP server
Browse files Browse the repository at this point in the history
  • Loading branch information
Pat Losoponkul committed Oct 21, 2022
1 parent c94ab5a commit bd3d99b
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ sealed trait PrismDID {
}

object PrismDID {
// TODO: implement a proper DID parser
// TODO: implement a proper DID parser (ATL-2031)
// For now, just make it work with a simple case of Prism DID V1
def parse(didRef: String): Either[String, PrismDID] = {
if (didRef.startsWith("did:prism:1:")) {
Expand Down
26 changes: 11 additions & 15 deletions prism-agent/api/http/castor/schemas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -206,24 +206,22 @@ components:
type: object
required:
- id
- purposes
- purpose
properties:
id:
type: string
description: Identifier of a verification material in the DID Document
example: key-01
purposes:
type: array
items:
type: string
enum:
[
"authentication",
"assertionMethod",
"keyAgreement",
"capabilityInvocation",
"capabilityDelegation",
]
purpose:
type: string
enum:
[
"authentication",
"assertionMethod",
"keyAgreement",
"capabilityInvocation",
"capabilityDelegation",
]
example: [ "authentication", "assertionMethod" ]
services:
type: array
Expand All @@ -236,8 +234,6 @@ components:
- did
- longFormDid
properties:
did:
$ref: "#/components/schemas/DID"
longFormDid:
type: string
description: A long-form DID for the created DID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
given RootJsonFormat[CreateManagedDidRequestDocumentTemplatePublicKeysInner] = jsonFormat2(
CreateManagedDidRequestDocumentTemplatePublicKeysInner.apply
)
given RootJsonFormat[CreateManagedDIDResponse] = jsonFormat2(CreateManagedDIDResponse.apply)
given RootJsonFormat[CreateManagedDIDResponse] = jsonFormat1(CreateManagedDIDResponse.apply)
given RootJsonFormat[DeactivateDIDRequest] = jsonFormat4(DeactivateDIDRequest.apply)
given RootJsonFormat[Delta] = jsonFormat2(Delta.apply)
given RootJsonFormat[DeltaUpdate] = jsonFormat2(DeltaUpdate.apply)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ package io.iohk.atala.agent.server.http.model

import io.iohk.atala.agent.openapi.model.{
CreateDIDRequest,
CreateManagedDidRequestDocumentTemplate,
CreateManagedDidRequestDocumentTemplatePublicKeysInner,
DIDOperationResponse,
DidOperation,
DidOperationSubmission,
JsonWebKey2020,
PublicKey,
PublicKeyJwk,
Service,
DidOperationSubmission
Service
}
import io.iohk.atala.castor.core.model.did as domain
import io.iohk.atala.castor.core.model.did as castorDomain
import io.iohk.atala.castor.core.model.did.PublishedDIDOperation
import io.iohk.atala.agent.walletapi.model as walletDomain
import io.iohk.atala.shared.models.HexStrings.*
import io.iohk.atala.shared.models.Base64UrlStrings.*
import io.iohk.atala.shared.utils.Traverse.*
Expand All @@ -22,7 +25,7 @@ import scala.util.Try
trait OASDomainModelHelper {

extension (req: CreateDIDRequest) {
def toDomain: Either[String, domain.PublishedDIDOperation.Create] = {
def toDomain: Either[String, castorDomain.PublishedDIDOperation.Create] = {
for {
updateCommitmentHex <- HexString
.fromString(req.updateCommitment)
Expand All @@ -36,25 +39,25 @@ trait OASDomainModelHelper {
.map(_ => "unable to convert recoveryCommitment to hex string")
publicKeys <- req.document.publicKeys.getOrElse(Nil).traverse(_.toDomain)
services <- req.document.services.getOrElse(Nil).traverse(_.toDomain)
} yield domain.PublishedDIDOperation.Create(
} yield castorDomain.PublishedDIDOperation.Create(
updateCommitment = updateCommitmentHex,
recoveryCommitment = recoveryCommitmentHex,
storage = domain.DIDStorage.Cardano(req.storage),
document = domain.DIDDocument(publicKeys = publicKeys, services = services)
storage = castorDomain.DIDStorage.Cardano(req.storage),
document = castorDomain.DIDDocument(publicKeys = publicKeys, services = services)
)
}
}

extension (service: Service) {
def toDomain: Either[String, domain.Service] = {
def toDomain: Either[String, castorDomain.Service] = {
for {
serviceEndpoint <- Try(URI.create(service.serviceEndpoint)).toEither.left.map(_ =>
s"unable to parse serviceEndpoint ${service.serviceEndpoint} as URI"
)
serviceType <- domain.ServiceType
serviceType <- castorDomain.ServiceType
.parseString(service.`type`)
.toRight(s"unsupported serviceType ${service.`type`}")
} yield domain.Service(
} yield castorDomain.Service(
id = service.id,
`type` = serviceType,
serviceEndpoint = serviceEndpoint
Expand All @@ -63,24 +66,24 @@ trait OASDomainModelHelper {
}

extension (key: PublicKey) {
def toDomain: Either[String, domain.PublicKey] = {
def toDomain: Either[String, castorDomain.PublicKey] = {
for {
purposes <- key.purposes.traverse(i =>
domain.VerificationRelationship
castorDomain.VerificationRelationship
.parseString(i)
.toRight(s"unsupported verificationRelationship $i")
)
publicKeyJwk <- key.jsonWebKey2020.publicKeyJwk.toDomain
} yield domain.PublicKey.JsonWebKey2020(id = key.id, purposes = purposes, publicKeyJwk = publicKeyJwk)
} yield castorDomain.PublicKey.JsonWebKey2020(id = key.id, purposes = purposes, publicKeyJwk = publicKeyJwk)
}
}

extension (jwk: PublicKeyJwk) {
def toDomain: Either[String, domain.PublicKeyJwk] = {
def toDomain: Either[String, castorDomain.PublicKeyJwk] = {
for {
crv <- jwk.crv
.toRight("expected crv field in JWK")
.flatMap(i => domain.EllipticCurve.parseString(i).toRight(s"unsupported curve $i"))
.flatMap(i => castorDomain.EllipticCurve.parseString(i).toRight(s"unsupported curve $i"))
x <- jwk.x
.toRight("expected x field in JWK")
.flatMap(
Expand All @@ -91,11 +94,37 @@ trait OASDomainModelHelper {
.flatMap(
Base64UrlString.fromString(_).toEither.left.map(_ => "unable to convert y coordinate to base64url string")
)
} yield domain.PublicKeyJwk.ECPublicKeyData(crv = crv, x = x, y = y)
} yield castorDomain.PublicKeyJwk.ECPublicKeyData(crv = crv, x = x, y = y)
}
}

extension (outcome: domain.PublishedDIDOperationOutcome) {
extension (template: CreateManagedDidRequestDocumentTemplate) {
def toDomain: Either[String, walletDomain.ManagedDIDTemplate] = {
for {
services <- template.services.traverse(_.toDomain)
publicKeys <- template.publicKeys.traverse(_.toDomain)
} yield walletDomain.ManagedDIDTemplate(
storage = template.storage,
publicKeys = publicKeys,
services = services
)
}
}

extension (publicKeyTemplate: CreateManagedDidRequestDocumentTemplatePublicKeysInner) {
def toDomain: Either[String, walletDomain.DIDPublicKeyTemplate] = {
for {
purpose <- castorDomain.VerificationRelationship
.parseString(publicKeyTemplate.purpose)
.toRight(s"unsupported verificationRelationship ${publicKeyTemplate.purpose}")
} yield walletDomain.DIDPublicKeyTemplate(
id = publicKeyTemplate.id,
purpose = purpose
)
}
}

extension (outcome: castorDomain.PublishedDIDOperationOutcome) {
def toOAS: DIDOperationResponse = DIDOperationResponse(
scheduledOperation = DidOperationSubmission(
id = outcome.operationId.toString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.iohk.atala.agent.server.http.model

import akka.http.scaladsl.server.StandardRoute
import io.iohk.atala.agent.openapi.model.ErrorResponse
import io.iohk.atala.agent.walletapi.model.error.PublishManagedDIDError
import io.iohk.atala.agent.walletapi.model.error.{CreateManagedDIDError, PublishManagedDIDError}
import io.iohk.atala.castor.core.model.error.DIDOperationError

trait ToErrorResponse[E] {
Expand Down Expand Up @@ -51,4 +51,16 @@ trait OASErrorModelHelper {
}
}

given ToErrorResponse[CreateManagedDIDError] with {
override def toErrorResponse(e: CreateManagedDIDError): ErrorResponse = {
ErrorResponse(
`type` = "error-type",
title = "error-title",
status = 500,
detail = Some(e.toString),
instance = "error-instance"
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,24 @@ class DIDRegistrarApiServiceImpl(service: ManagedDIDService)(using runtime: Runt
OASDomainModelHelper,
OASErrorModelHelper {

private val mockDID = DID(
id = "did:prism:1:mainnet:abcdef123456",
controller = None,
verificationMethod = None,
authentication = Some(
Seq(
VerificationMethodOrRef(verificationMethod =
Some(
VerificationMethod(
id = "did:prism:1:mainnet:abcdef123456#key-1",
`type` = "JsonWebKey2020",
controller = "did:prism:1:mainnet:abcdef123456",
jsonWebKey2020 = JsonWebKey2020(
publicKeyJwk = PublicKeyJwk(
crv = Some("P-256"),
x = Some("38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8"),
y = Some("nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4"),
kty = Some("EC"),
kid = Some("_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw")
)
)
)
)
)
)
),
assertionMethod = None,
keyAgreement = None,
capabilityInvocation = None,
service = None
)

// TODO: implement
override def createManagedDid(createManagedDidRequest: CreateManagedDidRequest)(implicit
toEntityMarshallerCreateManagedDIDResponse: ToEntityMarshaller[CreateManagedDIDResponse],
toEntityMarshallerErrorResponse: ToEntityMarshaller[ErrorResponse]
): Route = {
onZioSuccess(ZIO.unit) { _ =>
createManagedDid200(
CreateManagedDIDResponse(
did = mockDID,
longFormDid = "did:prism:1:mainnet:abcdef:abcdef"
)
)
val result = for {
didTemplate <- ZIO
.fromEither(createManagedDidRequest.documentTemplate.toDomain)
.mapError(HttpServiceError.InvalidPayload.apply)
longFormDID <- service
.createAndStoreDID(didTemplate)
.mapError(HttpServiceError.DomainError.apply)
} yield CreateManagedDIDResponse(
longFormDid = longFormDID.toString
)

onZioSuccess(result.mapError(_.toOAS).either) {
case Left(error) => complete(error.status -> error)
case Right(result) => createManagedDid200(result)
}
}

Expand Down

0 comments on commit bd3d99b

Please sign in to comment.