Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add e2e tests for different createCredentialOffer API variations #1476

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,11 @@ data class CreateIssueCredentialRecordRequest(
@SerializedName("goal")
val goal: kotlin.String? = null

// @SerializedName("jwtVcPropertiesV1")
// val jwtVcPropertiesV1: kotlin.collections.List<kotlin.String>? = null
// @SerializedName("jwtVcPropertiesV1")
// val anoncredsVcPropertiesV1: Option[AnonCredsVCPropertiesV1] = None
// @SerializedName("sdJwtVcPropertiesV1")
// val sdJwtVcPropertiesV1: Option[SDJWTVCPropertiesV1] = None
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
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.test.*
import zio.test.Assertion.*

object CredentialSchemaReferenceParsingLogicSpec extends ZIOSpecDefault with CredentialSchemaReferenceParsingLogic {

private val credentialSchemaExample = "http://example.com/schema"

private def isErrorResponseWithDetailFieldEqualTo(detail: String) =
isSubtype[ErrorResponse](hasField("detail", _.detail, isSome(equalTo(detail))))

def spec = suite("CredentialSchemaReferenceParsingLogic")(
suite("parseCredentialSchemaRef_VCDM1_1")(
test("should parse valid schema ref with correct type") {
val httpSchemaRef = HTTPCredentialSchemaRef(credentialSchemaExample, "JsonSchemaValidator2018")
for {
result <- parseCredentialSchemaRef_VCDM1_1(None, Some(httpSchemaRef)).either
expectedUriString <- UriString.make(credentialSchemaExample).toZIO
} yield assert(result)(
isRight(
equalTo(DomainCredentialSchemaRef(CredentialSchemaRefType.JsonSchemaValidator2018, expectedUriString))
)
)
},
test("should fail for schema ref with invalid type") {
val httpSchemaRef = HTTPCredentialSchemaRef(credentialSchemaExample, "InvalidType")
for {
result <- parseCredentialSchemaRef_VCDM1_1(None, Some(httpSchemaRef)).either
} yield assert(result)(
isLeft(isErrorResponseWithDetailFieldEqualTo("Invalid credentialSchema type: InvalidType."))
)
},
test("should parse deprecated schema ID property") {
for {
result <- parseCredentialSchemaRef_VCDM1_1(Some(credentialSchemaExample), None).either
expectedUriString <- UriString.make(credentialSchemaExample).toZIO
} yield assert(result)(
isRight(
equalTo(DomainCredentialSchemaRef(CredentialSchemaRefType.JsonSchemaValidator2018, expectedUriString))
)
)
},
test("should fail for multiple deprecated schema IDs") {
for {
result <- parseCredentialSchemaRef_VCDM1_1(Some(List("id1", "id2")), None).either
} yield assert(result)(
isLeft(isErrorResponseWithDetailFieldEqualTo("Multiple credential schemas are not allowed."))
)
},
test("should fail if no schema is provided") {
for {
result <- parseCredentialSchemaRef_VCDM1_1(None, None).either
} yield assert(result)(
isLeft(isErrorResponseWithDetailFieldEqualTo("Credential schema property missed."))
)
}
),
suite("parseSchemaIdForAnonCredsModelV1")(
test("should parse schema ID property") {
for {
result <- parseSchemaIdForAnonCredsModelV1(None, Some(credentialSchemaExample)).either
expectedUriString <- UriString.make(credentialSchemaExample).toZIO
} yield assert(result)(isRight(equalTo(expectedUriString)))
},
test("should parse deprecated schema ID property") {
for {
result <- parseSchemaIdForAnonCredsModelV1(Some(credentialSchemaExample), None).either
expectedUriString <- UriString.make(credentialSchemaExample).toZIO
} yield assert(result)(isRight(equalTo(expectedUriString)))
},
test("should fail for multiple deprecated schema IDs") {
for {
result <- parseSchemaIdForAnonCredsModelV1(Some(List("id1", "id2")), None).either
} yield assert(result)(
isLeft(isErrorResponseWithDetailFieldEqualTo("Multiple credential schemas are not allowed."))
)
},
test("should fail if no schema ID is provided") {
for {
result <- parseSchemaIdForAnonCredsModelV1(None, None).either
} yield assert(result)(
isLeft(isErrorResponseWithDetailFieldEqualTo("Credential schema property missed."))
)
}
)
)
}
2 changes: 1 addition & 1 deletion tests/integration-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dependencies {
testImplementation("io.ktor:ktor-server-netty:2.3.0")
testImplementation("io.ktor:ktor-client-apache:2.3.0")
// RestAPI client
testImplementation("org.hyperledger.identus:cloud-agent-client-kotlin:1.39.1-19ab426")
testImplementation("org.hyperledger.identus:cloud-agent-client-kotlin:1.40.1-248ba5f")
// Test helpers library
testImplementation("io.iohk.atala:atala-automation:0.4.0")
// Hoplite for configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package common

import org.hyperledger.identus.client.models.CreateIssueCredentialRecordRequest
import java.util.UUID

enum class CreateCredentialOfferAPIVersion {
V0 {
override fun buildCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
schemaUrl: String?,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double?,
): CreateIssueCredentialRecordRequest {
return CreateIssueCredentialRecordRequest(
schemaId = schemaUrl?.let { listOf(it) },
claims = claims,
issuingDID = did,
issuingKid = assertionKey,
connectionId = connectionId,
validityPeriod = validityPeriod ?: 3600.0,
credentialFormat = credentialType.format,
automaticIssuance = false,
)
}
},

// TODO: it's a copy/paste from the V0, I have to regenerate the Kotlin HTTP client
V1 {
override fun buildCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
schemaUrl: String?,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double?,
): CreateIssueCredentialRecordRequest {
return CreateIssueCredentialRecordRequest(
schemaId = schemaUrl?.let { listOf(it) },
claims = claims,
issuingDID = did,
issuingKid = assertionKey,
connectionId = connectionId,
validityPeriod = validityPeriod ?: 3600.0,
credentialFormat = credentialType.format,
automaticIssuance = false,
)
}
},
;

abstract fun buildCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
schemaUrl: String?,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double? = null,
): CreateIssueCredentialRecordRequest
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package common

enum class CredentialClaims {
STUDENT_CLAIMS {
override val claims: Map<String, Any> = linkedMapOf(
"name" to "Name",
"age" to 18,
)
},
ID_CLAIMS {
override val claims: Map<String, Any> = linkedMapOf(
"firstName" to "First Name",
"lastName" to "Last Name",
)
},
;

abstract val claims: Map<String, Any>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package common

enum class CredentialType {
JWT_VCDM_1_1 {
override val format: String = "JWT"
},
JWT_VCDM_2_0 {
override val format: String = "JWT"
},
ANONCREDS_V1 {
override val format: String = "AnonCreds"
},
SD_JWT_VCDM_1_1 {
override val format: String = "SD_JWT"
},
;

abstract val format: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ enum class JwtCredentialProblem {
override fun jwt(): String {
val jwt = VerifiableJwt.jwtVCv1()
jwt.audience("did:wrong")
return jwt.sign(DEFAULT_ALGORITHM, DEFAULT_CURVE)
return jwt.sign(defaultAlgorithm, defaultCurve)
}
override val verification = VcVerification.AUDIENCE_CHECK
},
Expand All @@ -33,7 +33,7 @@ enum class JwtCredentialProblem {
override fun jwt(): String {
val jwt = VerifiableJwt.jwtVCv1()
jwt.expirationTime(OffsetDateTime.now().plusYears(10))
return jwt.sign(DEFAULT_ALGORITHM, DEFAULT_CURVE)
return jwt.sign(defaultAlgorithm, defaultCurve)
}

override val verification = VcVerification.EXPIRATION_CHECK
Expand All @@ -48,15 +48,15 @@ enum class JwtCredentialProblem {
override fun jwt(): String {
val jwt = VerifiableJwt.jwtVCv1()
jwt.issuer("did:wrong")
return jwt.sign(DEFAULT_ALGORITHM, DEFAULT_CURVE)
return jwt.sign(defaultAlgorithm, defaultCurve)
}
override val verification = VcVerification.ISSUER_IDENTIFICATION
},
NOT_BEFORE_CHECK {
override fun jwt(): String {
val jwt = VerifiableJwt.jwtVCv1()
jwt.notBefore(OffsetDateTime.now().minusYears(10))
return jwt.sign(DEFAULT_ALGORITHM, DEFAULT_CURVE)
return jwt.sign(defaultAlgorithm, defaultCurve)
}
override val verification = VcVerification.NOT_BEFORE_CHECK
},
Expand All @@ -80,14 +80,14 @@ enum class JwtCredentialProblem {
claims.putAll(jwt.claimSetBuilder.claims)
claims.remove("iss")
jwtCredential.claims(claims)
return jwt.sign(DEFAULT_ALGORITHM, DEFAULT_CURVE)
return jwt.sign(defaultAlgorithm, defaultCurve)
}
override val verification = VcVerification.SEMANTIC_CHECK_OF_CLAIMS
},
SIGNATURE_VERIFICATION {
override fun jwt(): String {
val jwt = VerifiableJwt.jwtVCv1()
return jwt.sign(DEFAULT_ALGORITHM, DEFAULT_CURVE)
return jwt.sign(defaultAlgorithm, defaultCurve)
}
override val verification = VcVerification.SIGNATURE_VERIFICATION
},
Expand All @@ -112,8 +112,8 @@ enum class JwtCredentialProblem {
}
}

protected val DEFAULT_ALGORITHM = JWSAlgorithm.ES256K
protected val DEFAULT_CURVE = Curve.SECP256K1
protected val defaultAlgorithm = JWSAlgorithm.ES256K
protected val defaultCurve = Curve.SECP256K1

abstract fun jwt(): String
abstract val verification: VcVerification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import net.serenitybdd.screenplay.Actor

enum class SchemaErrorTemplate {
TYPE_AND_PROPERTIES_WITHOUT_SCHEMA_TYPE {
override fun inner_schema(): String {
override fun innerSchema(): String {
return """
{
"type": "object",
Expand All @@ -25,7 +25,7 @@ enum class SchemaErrorTemplate {
}
},
CUSTOM_WORDS_NOT_DEFINED {
override fun inner_schema(): String {
override fun innerSchema(): String {
return """
{
"${"$"}schema": "http://json-schema.org/draft-2020-12/schema#",
Expand All @@ -44,7 +44,7 @@ enum class SchemaErrorTemplate {
}
},
MISSING_REQUIRED_FOR_MANDATORY_PROPERTY {
override fun inner_schema(): String {
override fun innerSchema(): String {
return """
{
"${"$"}schema": "http://json-schema.org/draft-2020-12/schema#",
Expand All @@ -62,10 +62,10 @@ enum class SchemaErrorTemplate {
}
}, ;

abstract fun inner_schema(): String
abstract fun innerSchema(): String

fun schema(actor: Actor): String {
val innerSchema = Gson().fromJson(inner_schema(), JsonObject::class.java)
val innerSchema = Gson().fromJson(innerSchema(), JsonObject::class.java)
val json = getJson(actor)
json.add("schema", innerSchema)
return json.toString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.net.URI
import java.net.URL

/**
* https://github.com/eu-digital-identity-wallet/eudi-lib-jvm-openid4vci-kt/blob/e81802f3b90639b97e32a6fd1c06c20e5ff53f27/src/main/kotlin/eu/europa/ec/eudi/openid4vci/Types.kt#L45
* https://github.com/eu-digital-identity-wallet/eudi-lib-jvm-openid4vci-kt/blob/e81802f3b90639b97e32a6fd1c06c20e5ff53f27/src/main/kotlin/eu/europa/ec/eudi/openid4vci/HttpsUrl.kt#L45
*
* This overrides the implementation in EUDI to relax the HTTPS requirement making it easier for testing purpose
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,15 @@ class JwtCredential {
}

private fun parseKey(key: String): JWK {
try { return ECKey.parse(key) } catch (_: Error) { }
try { return OctetKeyPair.parse(key) } catch (_: Error) { }
throw RuntimeException("Invalid key [$key]")
return try {
ECKey.parse(key)
} catch (e: Exception) {
try {
OctetKeyPair.parse(key)
} catch (e: Exception) {
throw IllegalArgumentException("Invalid key [$key]", e)
}
}
}

private fun verifier(key: String): JWSVerifier {
Expand Down
Loading
Loading