Skip to content

Commit

Permalink
feat: align the credential schema property name according to the VCDM…
Browse files Browse the repository at this point in the history
… 1.1 (#1467)

Signed-off-by: Yurii Shynbuiev <[email protected]>
  • Loading branch information
yshyn-iohk authored Dec 4, 2024
1 parent 7365497 commit c6a3e0c
Show file tree
Hide file tree
Showing 40 changed files with 585 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ object StatusListCredential {
given stringOrCredentialIssuerDecoder: JsonDecoder[String | CredentialIssuer] =
JsonDecoder[CredentialIssuer]
.map(issuer => issuer: String | CredentialIssuer)
.orElse(JsonDecoder[String].map(schemaId => schemaId: String | CredentialIssuer))
.orElse(JsonDecoder[String].map(issuerId => issuerId: String | CredentialIssuer))

given statusListCredentialEncoder: JsonEncoder[StatusListCredential] =
DeriveJsonEncoder.gen[StatusListCredential]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.hyperledger.identus.issue.controller

import org.hyperledger.identus.api.http.ErrorResponse
import org.hyperledger.identus.issue.controller.http.CredentialSchemaRef as HTTPCredentialSchemaRef
import org.hyperledger.identus.pollux.core.model.primitives.UriString
import org.hyperledger.identus.pollux.core.model.schema.{
CredentialSchemaRef as DomainCredentialSchemaRef,
CredentialSchemaRefType
}
import zio.{IO, ZIO}

trait CredentialSchemaReferenceParsingLogic {

// According to VCDM 1.1, the property "credentialSchema" is required to issue JWT, JSON, and JSON-LD credentials.
// The "id" property in the "credentialSchema" object is a URI that points to the schema of the credential.
// The "type" property in the "credentialSchema" object must be "JsonSchemaValidator2018".
// Multiple schemas are not allowed in VCDM 1.1.
def parseCredentialSchemaRef_VCDM1_1(
deprecatedSchemaIdProperty: Option[String | List[String]],
credentialSchemaRefOption: Option[HTTPCredentialSchemaRef]
): IO[ErrorResponse, DomainCredentialSchemaRef] = {
credentialSchemaRefOption match {
case Some(csr) if csr.`type` == "JsonSchemaValidator2018" =>
makeDomainCredentialSchemaRef(csr.id)
case Some(csr) =>
ZIO.fail(ErrorResponse.badRequest(detail = Some(s"Invalid credentialSchema type: ${csr.`type`}.")))
case None =>
handleDeprecatedSchemaId(deprecatedSchemaIdProperty)
.flatMap(makeDomainCredentialSchemaRef)
}
}

def parseSchemaIdForAnonCredsModelV1(
deprecatedSchemaIdProperty: Option[String | List[String]],
schemaIdProperty: Option[String]
): IO[ErrorResponse, UriString] = {
schemaIdProperty
.map(makeUriStringOrErrorResponse)
.getOrElse(handleDeprecatedSchemaId(deprecatedSchemaIdProperty).flatMap(makeUriStringOrErrorResponse))
}

private def handleDeprecatedSchemaId(
deprecatedSchemaIdProperty: Option[String | List[String]]
): IO[ErrorResponse, String] = {
deprecatedSchemaIdProperty match {
case Some(schemaId: String) =>
ZIO.succeed(schemaId)
case Some(_: List[String]) =>
ZIO.fail(ErrorResponse.badRequest(detail = Some("Multiple credential schemas are not allowed.")))
case None =>
ZIO.fail(ErrorResponse.badRequest(detail = Some("Credential schema property missed.")))
}
}

private def makeDomainCredentialSchemaRef(input: String): IO[ErrorResponse, DomainCredentialSchemaRef] =
makeUriStringOrErrorResponse(input).map(
DomainCredentialSchemaRef(CredentialSchemaRefType.JsonSchemaValidator2018, _)
)

private def makeUriStringOrErrorResponse(input: String): IO[ErrorResponse, UriString] =
UriString.make(input).toZIO.mapError(uriParseError => ErrorResponse.badRequest(detail = Some(uriParseError)))
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class IssueControllerImpl(
managedDIDService: ManagedDIDService,
appConfig: AppConfig
) extends IssueController
with ControllerHelper {
with ControllerHelper
with CredentialSchemaReferenceParsingLogic {

private case class OfferContext(
pairwiseIssuerDID: DidId,
Expand All @@ -55,27 +56,29 @@ class IssueControllerImpl(
)

for {
jsonClaims <- ZIO // TODO: Get read of Circe and use zio-json all the way down
deprecatedJsonClaims <- ZIO // TODO: Get read of Circe and use zio-json all the way down
.fromEither(io.circe.parser.parse(request.claims.toString()))
.mapError(e => ErrorResponse.badRequest(detail = Some(e.getMessage)))
deprecatedSchemaId = request.schemaId
credentialFormat = request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT)
outcome <-
credentialFormat match
case JWT =>
for {
issuingDID <- getIssuingDidFromRequest(request)
_ <- validatePrismDID(issuingDID, allowUnpublished = true, Role.Issuer)
credentialSchemaRef <- parseCredentialSchemaRef_VCDM1_1(
deprecatedSchemaId,
request.jwtVcPropertiesV1.map(_.credentialSchema)
)
record <- credentialService
.createJWTIssueCredentialRecord(
pairwiseIssuerDID = offerContext.pairwiseIssuerDID,
pairwiseHolderDID = offerContext.pairwiseHolderDID,
kidIssuer = request.issuingKid,
thid = DidCommID(),
maybeSchemaIds = request.schemaId.map {
case schemaId: String => List(schemaId)
case schemaIds: List[String] => schemaIds
},
claims = jsonClaims,
credentialSchemaRef = Some(credentialSchemaRef),
claims = deprecatedJsonClaims,
validityPeriod = request.validityPeriod,
automaticIssuance = request.automaticIssuance.orElse(Some(true)),
issuingDID = issuingDID.asCanonical,
Expand All @@ -89,17 +92,18 @@ class IssueControllerImpl(
for {
issuingDID <- getIssuingDidFromRequest(request)
_ <- validatePrismDID(issuingDID, allowUnpublished = true, Role.Issuer)
credentialSchemaRef <- parseCredentialSchemaRef_VCDM1_1(
deprecatedSchemaId,
request.sdJwtVcPropertiesV1.map(_.credentialSchema)
)
record <- credentialService
.createSDJWTIssueCredentialRecord(
pairwiseIssuerDID = offerContext.pairwiseIssuerDID,
pairwiseHolderDID = offerContext.pairwiseHolderDID,
kidIssuer = request.issuingKid,
thid = DidCommID(),
maybeSchemaIds = request.schemaId.map {
case schemaId: String => List(schemaId)
case schemaIds: List[String] => schemaIds
},
claims = jsonClaims,
credentialSchemaRef = Option(credentialSchemaRef),
claims = deprecatedJsonClaims,
validityPeriod = request.validityPeriod,
automaticIssuance = request.automaticIssuance.orElse(Some(true)),
issuingDID = issuingDID.asCanonical,
Expand Down Expand Up @@ -160,7 +164,7 @@ class IssueControllerImpl(
thid = DidCommID(),
credentialDefinitionGUID = credentialDefinitionGUID,
credentialDefinitionId = credentialDefinitionId,
claims = jsonClaims,
claims = deprecatedJsonClaims,
validityPeriod = request.validityPeriod,
automaticIssuance = request.automaticIssuance.orElse(Some(true)),
goalCode = offerContext.goalCode,
Expand Down
Loading

0 comments on commit c6a3e0c

Please sign in to comment.