diff --git a/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerification.scala b/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerification.scala index 4d766a4a0d..f0e616a795 100644 --- a/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerification.scala +++ b/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerification.scala @@ -160,6 +160,13 @@ object PresentationSubmissionVerification { } } yield () } + .catchAll { errors => + // if all paths don't satisfy constraints, but optional, then the field is still valid + // https://identity.foundation/presentation-exchange/spec/v2.1.1/#input-evaluation + if field.optional.getOrElse(false) + then ZIO.unit + else ZIO.fail(errors) + } .mapError(_ => ClaimNotSatisfyInputConstraint(descriptor.id)) } .unit diff --git a/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerificationSpec.scala b/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerificationSpec.scala index a1618cf104..cde4360ddb 100644 --- a/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerificationSpec.scala +++ b/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationSubmissionVerificationSpec.scala @@ -153,7 +153,7 @@ object PresentationSubmissionVerificationSpec extends ZIOSpecDefault { val jwtVc = generateJwtVc(payload) assertSubmissionVerification("[]", "[]", jwtVc)(succeeds(anything)) }, - test("one matching descriptor and submission") { + test("one descriptor and corresponding submission") { val payload = generateVcPayload(parseUnsafe("""{"name": "alice"}""")) val jwtVc = generateJwtVc(payload) assertSubmissionVerification( @@ -435,11 +435,64 @@ object PresentationSubmissionVerificationSpec extends ZIOSpecDefault { """.stripMargin, jwtVp )(succeeds(anything)) + }, + test("descriptor with optional field and submission that omit optional fields") { + val payload = generateVcPayload(parseUnsafe("""{"gpa": 4.00}""")) + val jwtVc = generateJwtVc(payload) + assertSubmissionVerification( + """[ + | { + | "id": "university_degree", + | "constraints": { + | "fields": [ + | { + | "path": ["$.vc.credentialSubject.name"], + | "optional": true + | } + | ] + | } + | } + |] + """.stripMargin, + """[ + | {"id": "university_degree", "format": "jwt_vc", "path": "$"} + |] + """.stripMargin, + jwtVc + )(succeeds(anything)) + }, + test("descriptor with optional field and submission with optional fields that don't satisfy constraints") { + val payload = generateVcPayload(parseUnsafe("""{"name": "alice"}""")) + val jwtVc = generateJwtVc(payload) + assertSubmissionVerification( + """[ + | { + | "id": "university_degree", + | "constraints": { + | "fields": [ + | { + | "path": ["$.vc.credentialSubject.name"], + | "optional": true, + | "filter": { + | "type": "string", + | "const": "bob" + | } + | } + | ] + | } + | } + |] + """.stripMargin, + """[ + | {"id": "university_degree", "format": "jwt_vc", "path": "$"} + |] + """.stripMargin, + jwtVc + )(succeeds(anything)) } - /* TODO - * - test for optional field - * - test for format verification failure - */ ) + /* TODO + * - test for format verification failure + */ }