Skip to content

Commit

Permalink
feat: interoperable schema changes (#870)
Browse files Browse the repository at this point in the history
Signed-off-by: Bassam Riman <[email protected]>
Signed-off-by: Benjamin Voiturier <[email protected]>
Co-authored-by: bvoiturier <[email protected]>
Signed-off-by: Shota Jolbordi <[email protected]>
  • Loading branch information
2 people authored and Shota Jolbordi committed Mar 18, 2024
1 parent 6721943 commit 8fcaa7a
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.iohk.atala.pollux.core.service.URIDereferencer
import zio.*
import zio.json.*
import zio.json.ast.Json
import zio.prelude.Validation

import java.net.URI
import java.time.{OffsetDateTime, ZoneOffset}
Expand Down Expand Up @@ -125,17 +124,19 @@ object CredentialSchema {
for {
uri <- ZIO.attempt(new URI(schemaId)).mapError(t => URISyntaxError(t.getMessage))
content <- uriDereferencer.dereference(uri).mapError(err => UnexpectedError(err.toString))
vcSchema <- parseCredentialSchema(content)
resolvedSchemaType <- resolveCredentialSchemaType(vcSchema.`type`)
_ <-
Validation
.fromPredicateWith(
CredentialSchemaParsingError(
s"Only ${CredentialJsonSchemaType.`type`} schema type can be used to verify claims"
)
)(resolvedSchemaType.`type`)(`type` => `type` == CredentialJsonSchemaType.`type`)
.toZIO
schemaValidator <- JsonSchemaValidatorImpl.from(vcSchema.schema).mapError(SchemaError.apply)
json <- ZIO
.fromEither(content.fromJson[Json])
.mapError(error =>
CredentialSchemaError.CredentialSchemaParsingError(s"Failed to parse resolved schema content as Json: $error")
)
schemaValidator <- JsonSchemaValidatorImpl
.from(json)
.orElse(
ZIO
.fromEither(json.as[CredentialSchema])
.mapError(error => CredentialSchemaParsingError(s"Failed to parse schema content as Json or OEA: $error"))
.flatMap(cs => JsonSchemaValidatorImpl.from(cs.schema).mapError(SchemaError.apply))
)
_ <- schemaValidator.validate(claims).mapError(SchemaError.apply)
} yield ()
}
Expand All @@ -148,20 +149,10 @@ object CredentialSchema {
for {
uri <- ZIO.attempt(new URI(schemaId)).mapError(t => URISyntaxError(t.getMessage))
content <- uriDereferencer.dereference(uri).mapError(err => UnexpectedError(err.toString))
vcSchema <- parseCredentialSchema(content)
resolvedSchemaType <- resolveCredentialSchemaType(vcSchema.`type`)
_ <-
Validation
.fromPredicateWith(
CredentialSchemaParsingError(
s"Only ${CredentialJsonSchemaType.`type`} schema type can be used to verify claims"
)
)(resolvedSchemaType.`type`)(`type` => `type` == AnoncredSchemaType.`type`)
.toZIO
validAttrNames <- ZIO
.fromEither(vcSchema.schema.as[AnoncredSchemaSerDesV1])
.fromEither(content.fromJson[AnoncredSchemaSerDesV1])
.mapError(error => CredentialSchemaParsingError(s"AnonCreds Schema parsing error: $error"))
.map(_.attrNames)
.mapError(err => UnexpectedError(err))
jsonClaims <- ZIO.fromEither(claims.fromJson[Json]).mapError(err => UnexpectedError(err))
_ <- jsonClaims match
case Json.Obj(fields) =>
Expand Down Expand Up @@ -193,8 +184,4 @@ object CredentialSchema {
} yield ()
}

def parseCredentialSchema(vcSchemaString: String): IO[CredentialSchemaError, CredentialSchema] =
ZIO
.fromEither(vcSchemaString.fromJson[CredentialSchema])
.mapError(error => CredentialSchemaParsingError(s"VC Schema parsing error: $error"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.pollux.anoncreds.{AnoncredLib, SchemaDef}
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError.URISyntaxError
import io.iohk.atala.pollux.core.model.schema.CredentialDefinition
import io.iohk.atala.pollux.core.model.schema.CredentialDefinition.{Filter, FilteredEntries}
import io.iohk.atala.pollux.core.model.schema.CredentialSchema.parseCredentialSchema
import io.iohk.atala.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
import io.iohk.atala.pollux.core.model.schema.validator.JsonSchemaError
import io.iohk.atala.pollux.core.model.schema.{CredentialDefinition, CredentialSchema}
import io.iohk.atala.pollux.core.model.secret.CredentialDefinitionSecret
import io.iohk.atala.pollux.core.repository.CredentialDefinitionRepository
import io.iohk.atala.pollux.core.repository.Repository.SearchQuery
Expand All @@ -36,8 +35,7 @@ class CredentialDefinitionServiceImpl(
for {
uri <- ZIO.attempt(new URI(in.schemaId))
content <- uriDereferencer.dereference(uri)
vcSchema <- parseCredentialSchema(content)
anoncredSchema <- AnoncredSchemaSerDesV1.schemaSerDes.deserialize(vcSchema.schema)
anoncredSchema <- AnoncredSchemaSerDesV1.schemaSerDes.deserialize(content)
anoncredLibSchema =
SchemaDef(
in.schemaId,
Expand Down
34 changes: 9 additions & 25 deletions pollux/lib/core/src/test/resources/anoncred-schema-example.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
{
"guid": "1631026d-5d55-3285-8ccd-bd70480cfbdc",
"id": "329da384-b2bb-497f-a605-4118dec75d31",
"longId": "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff/329da384-b2bb-497f-a605-4118dec75d31?version=5.0.0",
"name": "DrivingLicense",
"version": "5.0.0",
"tags": [
"string"
"name": "Driving licence Anoncred Schema",
"version": "1.0",
"attrNames": [
"emailAddress",
"familyName",
"dateOfIssuance",
"drivingLicenseID",
"drivingClass"
],
"description": "Simple credential schema for the driving licence verifiable credential.",
"type": "AnoncredSchemaV1",
"schema": {
"name": "Driving licence Anoncred Schema",
"version": "1.0",
"attrNames": [
"emailAddress",
"familyName",
"dateOfIssuance",
"drivingLicenseID",
"drivingClass"
],
"issuerId": "http://www.example.com/issuer"
},
"author": "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff",
"authored": "2023-04-06T08:48:01.654162Z",
"kind": "CredentialSchema",
"self": "/schema-registry/schemas/1631026d-5d55-3285-8ccd-bd70480cfbdc"
"issuerId": "http://www.example.com/issuer"
}
98 changes: 41 additions & 57 deletions pollux/lib/core/src/test/resources/vc-schema-example.json
Original file line number Diff line number Diff line change
@@ -1,60 +1,44 @@
{
"guid": "1631026d-5d55-3285-8ccd-bd70480cfbdc",
"id": "329da384-b2bb-497f-a605-4118dec75d31",
"longId": "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff/329da384-b2bb-497f-a605-4118dec75d31?version=5.0.0",
"name": "DrivingLicense",
"version": "5.0.0",
"tags": [
"string"
],
"description": "Simple credential schema for the driving licence verifiable credential.",
"type": "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Driving License",
"type": "object",
"properties": {
"credentialSubject": {
"type": "object",
"properties": {
"emailAddress": {
"type": "string",
"format": "email"
},
"givenName": {
"type": "string"
},
"familyName": {
"type": "string"
},
"dateOfIssuance": {
"type": "string",
"format": "date-time"
},
"drivingLicenseID": {
"type": "string"
},
"drivingClass": {
"type": "integer"
}
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Driving License",
"type": "object",
"properties": {
"credentialSubject": {
"type": "object",
"properties": {
"emailAddress": {
"type": "string",
"format": "email"
},
"givenName": {
"type": "string"
},
"familyName": {
"type": "string"
},
"required": [
"emailAddress",
"familyName",
"dateOfIssuance",
"drivingLicenseID",
"drivingClass"
],
"additionalProperties": false
}
},
"required": [
"credentialSubject"
],
"additionalProperties": false
"dateOfIssuance": {
"type": "string",
"format": "date-time"
},
"drivingLicenseID": {
"type": "string"
},
"drivingClass": {
"type": "integer"
}
},
"required": [
"emailAddress",
"familyName",
"dateOfIssuance",
"drivingLicenseID",
"drivingClass"
],
"additionalProperties": false
}
},
"author": "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff",
"authored": "2023-04-06T08:48:01.654162Z",
"kind": "CredentialSchema",
"self": "/schema-registry/schemas/1631026d-5d55-3285-8ccd-bd70480cfbdc"
}
"required": [
"credentialSubject"
],
"additionalProperties": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.iohk.atala.pollux.credentialschema.http.{
import sttp.apispec.{ExternalDocumentation, Tag}
import sttp.model.StatusCode
import sttp.tapir.json.zio.jsonBody
import sttp.tapir.json.zio.schemaForZioJsonValue
import sttp.tapir.{
Endpoint,
EndpointInput,
Expand All @@ -28,6 +29,7 @@ import sttp.tapir.{
statusCode,
stringToPath
}
import zio.json.ast.Json

import java.util.UUID

Expand Down Expand Up @@ -161,6 +163,26 @@ object SchemaRegistryEndpoints {
)
.tag(tagName)

val getRawSchemaByIdEndpoint: PublicEndpoint[
(RequestContext, UUID),
ErrorResponse,
Json, // changed to generic Json type
Any
] =
endpoint.get
.in(extractFromRequest[RequestContext](RequestContext.apply))
.in(
"schema-registry" / "schemas" / path[UUID]("guid") / "schema".description(
"Globally unique identifier of the credential schema record"
)
)
.out(jsonBody[Json].description("Raw JSON response of the CredentialSchema")) // changed to Json
.errorOut(basicFailuresAndNotFound)
.name("getRawSchemaById")
.summary("Fetch the schema from the registry by `guid`")
.description("Fetch the credential schema by the unique identifier")
.tag("Schema Registry")

private val credentialSchemaFilterInput: EndpointInput[FilterInput] = EndpointInput.derived[FilterInput]
private val paginationInput: EndpointInput[PaginationInput] = EndpointInput.derived[PaginationInput]
val lookupSchemasByQueryEndpoint: Endpoint[
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package io.iohk.atala.pollux.credentialschema

import io.iohk.atala.agent.walletapi.model.BaseEntity
import io.iohk.atala.api.http.RequestContext
import io.iohk.atala.api.http.model.{Order, PaginationInput}
import io.iohk.atala.api.http.{ErrorResponse, RequestContext}
import io.iohk.atala.iam.authentication.Authenticator
import io.iohk.atala.iam.authentication.Authorizer
import io.iohk.atala.iam.authentication.DefaultAuthenticator
import io.iohk.atala.iam.authentication.SecurityLogic
import io.iohk.atala.iam.authentication.{Authenticator, Authorizer, DefaultAuthenticator, SecurityLogic}
import io.iohk.atala.pollux.credentialschema.SchemaRegistryEndpoints.*
import io.iohk.atala.pollux.credentialschema.controller.CredentialSchemaController
import io.iohk.atala.pollux.credentialschema.http.{CredentialSchemaInput, FilterInput}
Expand All @@ -21,14 +18,11 @@ class SchemaRegistryServerEndpoints(
authenticator: Authenticator[BaseEntity],
authorizer: Authorizer[BaseEntity]
) {
def throwableToInternalServerError(throwable: Throwable) =
ZIO.fail[ErrorResponse](ErrorResponse.internalServerError(detail = Option(throwable.getMessage)))

val createSchemaServerEndpoint: ZServerEndpoint[Any, Any] =
createSchemaEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic {
case wac => { case (ctx: RequestContext, schemaInput: CredentialSchemaInput) =>
.serverLogic { wac =>
{ case (ctx: RequestContext, schemaInput: CredentialSchemaInput) =>
credentialSchemaController
.createSchema(schemaInput)(ctx)
.provideSomeLayer(ZLayer.succeed(wac))
Expand All @@ -38,8 +32,8 @@ class SchemaRegistryServerEndpoints(
val updateSchemaServerEndpoint: ZServerEndpoint[Any, Any] =
updateSchemaEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic {
case wac => { case (ctx: RequestContext, author: String, id: UUID, schemaInput: CredentialSchemaInput) =>
.serverLogic { wac =>
{ case (ctx: RequestContext, author: String, id: UUID, schemaInput: CredentialSchemaInput) =>
credentialSchemaController
.updateSchema(author, id, schemaInput)(ctx)
.provideSomeLayer(ZLayer.succeed(wac))
Expand All @@ -52,11 +46,17 @@ class SchemaRegistryServerEndpoints(
credentialSchemaController.getSchemaByGuid(guid)(ctx)
}

val getRawSchemaByIdServerEndpoint: ZServerEndpoint[Any, Any] =
getRawSchemaByIdEndpoint
.zServerLogic { case (ctx: RequestContext, guid: UUID) =>
credentialSchemaController.getSchemaJsonByGuid(guid)(ctx)
}

val lookupSchemasByQueryServerEndpoint: ZServerEndpoint[Any, Any] =
lookupSchemasByQueryEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic {
case wac => {
.serverLogic { wac =>
{
case (
ctx: RequestContext,
filter: FilterInput,
Expand All @@ -78,6 +78,7 @@ class SchemaRegistryServerEndpoints(
createSchemaServerEndpoint,
updateSchemaServerEndpoint,
getSchemaByIdServerEndpoint,
getRawSchemaByIdServerEndpoint,
lookupSchemasByQueryServerEndpoint
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import io.iohk.atala.pollux.credentialschema.http.{
FilterInput
}
import zio.*
import scala.language.implicitConversions

import scala.language.implicitConversions
import java.util.UUID
import io.iohk.atala.shared.models.WalletAccessContext
import zio.json.ast.Json

trait CredentialSchemaController {
def createSchema(in: CredentialSchemaInput)(implicit
Expand All @@ -29,6 +30,10 @@ trait CredentialSchemaController {
rc: RequestContext
): IO[ErrorResponse, CredentialSchemaResponse]

def getSchemaJsonByGuid(id: UUID)(implicit
rc: RequestContext
): IO[ErrorResponse, Json]

def delete(guid: UUID)(implicit
rc: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse]
Expand Down
Loading

0 comments on commit 8fcaa7a

Please sign in to comment.