Skip to content

Commit

Permalink
refactor: sdjwt e2e test, exchange-api, verifier-api
Browse files Browse the repository at this point in the history
# Conflicts:
#	waltid-services/waltid-e2e-tests/src/test/kotlin/E2ETest.kt
  • Loading branch information
mikeplotean committed Aug 13, 2024
1 parent 152deab commit fa39a60
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 131 deletions.
92 changes: 92 additions & 0 deletions waltid-services/waltid-e2e-tests/src/test/kotlin/E2ESdJwtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import E2ETest.Companion.nameFieldSchemaPresentationRequestPayload
import E2ETest.Companion.sdjwtCredential
import id.walt.issuer.issuance.IssuanceRequest
import id.walt.oid4vc.data.dif.PresentationDefinition
import id.walt.webwallet.db.models.WalletCredential
import id.walt.webwallet.web.controllers.UsePresentationRequest
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.util.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.uuid.UUID

class E2ESdJwtTest(
private val issuerApi: IssuerApi,
private val exchangeApi: ExchangeApi,
private val sessionApi: Verifier.SessionApi,
private val verificationApi: Verifier.VerificationApi,
) {

fun e2e(wallet: UUID, did: String) = runTest {
//region -Issuer / offer url-
lateinit var offerUrl: String
val issuanceRequest = Json.decodeFromJsonElement<IssuanceRequest>(sdjwtCredential)
println("issuance-request:")
println(issuanceRequest)
issuerApi.sdjwt(issuanceRequest) {
offerUrl = it
println("offer: $offerUrl")
}
//endregion -Issuer / offer url-

//region -Exchange / claim-
lateinit var newCredential: WalletCredential
exchangeApi.resolveCredentialOffer(wallet, offerUrl)
exchangeApi.useOfferRequest(wallet, offerUrl, 1) {
newCredential = it.first()
}
//endregion -Exchange / claim-

//region -Verifier / request url-
lateinit var verificationUrl: String
lateinit var verificationId: String
verificationApi.verify(nameFieldSchemaPresentationRequestPayload) {
verificationUrl = it
verificationId = Url(verificationUrl).parameters.getOrFail("state")
}
//endregion -Verifier / request url-

//region -Exchange / presentation-
lateinit var resolvedPresentationOfferString: String
lateinit var presentationDefinition: String
exchangeApi.resolvePresentationRequest(wallet, verificationUrl) {
resolvedPresentationOfferString = it
presentationDefinition = Url(it).parameters.getOrFail("presentation_definition")
}

sessionApi.get(verificationId) {
assert(it.presentationDefinition == PresentationDefinition.fromJSONString(presentationDefinition))
}

exchangeApi.matchCredentialsForPresentationDefinition(
wallet, presentationDefinition, listOf(newCredential.id)
)
exchangeApi.unmatchedCredentialsForPresentationDefinition(wallet, presentationDefinition)
exchangeApi.usePresentationRequest(
wallet = wallet,
request = UsePresentationRequest(
did = did,
presentationRequest = resolvedPresentationOfferString,
selectedCredentials = listOf(newCredential.id),
disclosures = newCredential.disclosures?.let { mapOf(newCredential.id to listOf(it)) },
),
expectStatus = HttpResponse::expectFailure,
)

sessionApi.get(verificationId) {
assert(it.tokenResponse?.vpToken?.jsonPrimitive?.contentOrNull?.expectLooksLikeJwt() != null) { "Received no valid token response!" }
assert(it.tokenResponse?.presentationSubmission != null) { "should have a presentation submission after submission" }

assert(it.verificationResult == false) { "overall verification should be valid" }
it.policyResults.let {
require(it != null) { "policyResults should be available after running policies" }
assert(it.size > 1) { "no policies have run" }
}
}
//endregion -Exchange / presentation-
}
}
159 changes: 39 additions & 120 deletions waltid-services/waltid-e2e-tests/src/test/kotlin/E2ETest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import id.walt.issuer.issuance.IssuanceRequest
import id.walt.oid4vc.data.dif.PresentationDefinition
import id.walt.webwallet.config.RegistrationDefaultsConfig
import id.walt.webwallet.db.models.AccountWalletListing
import id.walt.webwallet.web.controllers.UsePresentationRequest
import id.walt.webwallet.web.model.AccountRequest
import id.walt.webwallet.web.model.EmailAccountRequest
import io.ktor.client.*
Expand All @@ -29,30 +30,32 @@ import kotlin.time.Duration.Companion.minutes

class E2ETest {

private val defaultTestTimeout = 5.minutes
private val defaultEmailAccount = EmailAccountRequest(
email = "user@email.com",
password = "password"
)
private val issuerKey = loadResource("issuance/key.json")
private val issuerDid = loadResource("issuance/did.txt")
private val openBadgeCredentialData = loadResource("issuance/openbadgecredential.json")
private val credentialMapping = loadResource("issuance/mapping.json")
private val credentialDisclosure = loadResource("issuance/disclosure.json")
private val jwtCredential = buildJsonObject {
put("issuerKey", Json.decodeFromString<JsonElement>(issuerKey))
put("issuerDid", issuerDid)
put("credentialConfigurationId", "OpenBadgeCredential_jwt_vc_json")
put("credentialData", Json.decodeFromString<JsonElement>(openBadgeCredentialData))
put("mapping", Json.decodeFromString<JsonElement>(credentialMapping))
}
private val sdjwtCredential = JsonObject(
jwtCredential.plus(
Pair(//todo: converted to map
"selectiveDisclosure", Json.decodeFromString<JsonElement>(credentialDisclosure)
)
companion object {
val defaultTestTimeout = 5.minutes
val defaultEmailAccount = EmailAccountRequest(
email = "user@email.com",
password = "password"
)
)
val issuerKey = loadResource("issuance/key.json")
val issuerDid = loadResource("issuance/did.txt")
val openBadgeCredentialData = loadResource("issuance/openbadgecredential.json")
val credentialMapping = loadResource("issuance/mapping.json")
val credentialDisclosure = loadResource("issuance/disclosure.json")
val sdjwtCredential = buildJsonObject {
put("issuerKey", Json.decodeFromString<JsonElement>(issuerKey))
put("issuerDid", issuerDid)
put("credentialConfigurationId", "OpenBadgeCredential_jwt_vc_json")
put("credentialData", Json.decodeFromString<JsonElement>(openBadgeCredentialData))
put("mapping", Json.decodeFromString<JsonElement>(credentialMapping))
put("selectiveDisclosure", Json.decodeFromString<JsonElement>(credentialDisclosure))
}
val jwtCredential = JsonObject(sdjwtCredential.minus("selectiveDisclosure"))
val simplePresentationRequestPayload =
loadResource("presentation/openbadgecredential-presentation-request.json")
val nameFieldSchemaPresentationRequestPayload =
loadResource("presentation/openbadgecredential-name-field-presentation-request.json")
}


@Test
fun e2e() = testBlock(defaultTestTimeout) {
Expand Down Expand Up @@ -199,12 +202,9 @@ class E2ETest {
lateinit var verificationId: String
val sessionApi = Verifier.SessionApi(client)
val verificationApi = Verifier.VerificationApi(client)
verificationApi.verify(loadResource("presentation/openbadgecredential-presentation-request.json")) {
verificationApi.verify(simplePresentationRequestPayload) {
verificationUrl = it
assert(verificationUrl.contains("presentation_definition_uri="))
assert(!verificationUrl.contains("presentation_definition="))
verificationId = Url(verificationUrl).parameters.getOrFail("state")
verificationUrl
}
//endregion -Verifier / request url-

Expand All @@ -224,7 +224,9 @@ class E2ETest {
wallet, presentationDefinition, listOf(newCredentialId)
)
exchangeApi.unmatchedCredentialsForPresentationDefinition(wallet, presentationDefinition)
exchangeApi.usePresentationRequest(wallet, did, resolvedPresentationOfferString, listOf(newCredentialId))
exchangeApi.usePresentationRequest(
wallet, UsePresentationRequest(did, resolvedPresentationOfferString, listOf(newCredentialId))
)

sessionApi.get(verificationId) {
assert(it.tokenResponse?.vpToken?.jsonPrimitive?.contentOrNull?.expectLooksLikeJwt() != null) { "Received no valid token response!" }
Expand Down Expand Up @@ -257,104 +259,16 @@ class E2ETest {
assert(it.any { it.operation == "useOfferRequest" } && it.any { it.operation == "usePresentationRequest" }) { "incorrect history items" }
}
//endregion -History-
val sdJwtTest = E2ESdJwtTest(issuerApi, exchangeApi, sessionApi, verificationApi)
//cleanup credentials
credentialsApi.delete(wallet, newCredentialId)
sdJwtTest.e2e(wallet, did)

// Test Authorization Code flow with available authentication methods in Issuer API
val authorizationCodeFlow = AuthorizationCodeFlow(testHttpClient(doFollowRedirects = false))
authorizationCodeFlow.testIssuerAPI()
}

@Test
fun e2eSdJwt() = testBlock(defaultTestTimeout) {
var client = testHttpClient()
lateinit var accountId: UUID
lateinit var wallet: UUID
//region -Login-
var authApi = AuthApi(client)
authApi.login(defaultEmailAccount) {
client = testHttpClient(token = it["token"]!!.jsonPrimitive.content)
accountId = UUID(it["id"]!!.jsonPrimitive.content)
authApi = AuthApi(client)
}
authApi.userSession()
authApi.userWallets(accountId) {
wallet = it.wallets.first().id
println("Selected wallet: $wallet")
}
//endregion -Login-

val didsApi = DidsApi(client)
lateinit var did: String
didsApi.list(wallet, DidsApi.DefaultDidOption.Any) {
did = it.first { it.default }.did
}

//region -Issuer / offer url-
lateinit var offerUrl: String
val issuerApi = IssuerApi(client)
val issuanceRequest = Json.decodeFromJsonElement<IssuanceRequest>(sdjwtCredential)
println("issuance-request:")
println(issuanceRequest)
issuerApi.sdjwt(issuanceRequest) {
offerUrl = it
println("offer: $offerUrl")
}
//endregion -Issuer / offer url-

//region -Exchange / claim-
val exchangeApi = ExchangeApi(client)
lateinit var newCredentialId: String
exchangeApi.resolveCredentialOffer(wallet, offerUrl)
exchangeApi.useOfferRequest(wallet, offerUrl, 1) {
val cred = it.first()
newCredentialId = cred.id
}
//endregion -Exchange / claim-

//region -Verifier / request url-
lateinit var verificationUrl: String
lateinit var verificationId: String
val sessionApi = Verifier.SessionApi(client)
val verificationApi = Verifier.VerificationApi(client)
verificationApi.verify(loadResource("presentation/openbadgecredential-presentation-request.json")) {
verificationUrl = it
assert(verificationUrl.contains("presentation_definition_uri="))
assert(!verificationUrl.contains("presentation_definition="))
verificationId = Url(verificationUrl).parameters.getOrFail("state")
verificationUrl
}
//endregion -Verifier / request url-

//region -Exchange / presentation-
lateinit var resolvedPresentationOfferString: String
lateinit var presentationDefinition: String
exchangeApi.resolvePresentationRequest(wallet, verificationUrl) {
resolvedPresentationOfferString = it
presentationDefinition = Url(it).parameters.getOrFail("presentation_definition")
}

sessionApi.get(verificationId) {
assert(it.presentationDefinition == PresentationDefinition.fromJSONString(presentationDefinition))
}

exchangeApi.matchCredentialsForPresentationDefinition(
wallet, presentationDefinition, listOf(newCredentialId)
)
exchangeApi.unmatchedCredentialsForPresentationDefinition(wallet, presentationDefinition)
exchangeApi.usePresentationRequest(wallet, did, resolvedPresentationOfferString, listOf(newCredentialId))

sessionApi.get(verificationId) {
assert(it.tokenResponse?.vpToken?.jsonPrimitive?.contentOrNull?.expectLooksLikeJwt() != null) { "Received no valid token response!" }
assert(it.tokenResponse?.presentationSubmission != null) { "should have a presentation submission after submission" }

assert(it.verificationResult == true) { "overall verification should be valid" }
it.policyResults.let {
require(it != null) { "policyResults should be available after running policies" }
assert(it.size > 1) { "no policies have run" }
}
}
//endregion -Exchange / presentation-
}


//@Test // enable to execute test selectively
fun lspIssuanceTests() = testBlock(timeout = defaultTestTimeout) {
Expand Down Expand Up @@ -433,6 +347,11 @@ fun HttpResponse.expectRedirect(): HttpResponse = also {
assert(status == HttpStatusCode.Found) { "HTTP status is non-successful" }
}

//todo: temporary
fun HttpResponse.expectFailure(): HttpResponse = also {
assert(!status.isSuccess()) { "HTTP status is successful" }
}

fun JsonElement.tryGetData(key: String): JsonElement? = key.split('.').let {
var element: JsonElement? = this
for (i in it) {
Expand Down
15 changes: 4 additions & 11 deletions waltid-services/waltid-e2e-tests/src/test/kotlin/ExchangeApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,11 @@ class ExchangeApi(private val client: HttpClient) {

suspend fun usePresentationRequest(
wallet: UUID,
did: String? = null,
presentationRequestUrl: String,
credentialIds: List<String>
request: UsePresentationRequest,
expectStatus: HttpResponse.() -> HttpResponse = HttpResponse::expectSuccess
) = test("/wallet-api/wallet/{wallet}/exchange/usePresentationRequest - present credentials") {
client.post("/wallet-api/wallet/$wallet/exchange/usePresentationRequest") {
setBody(
UsePresentationRequest(
did = did,
presentationRequest = presentationRequestUrl,
selectedCredentials = credentialIds,
)
)
}.expectSuccess()
setBody(request)
}.expectStatus()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ object Verifier {
client.post("/openid4vc/verify") {
setBody(Json.decodeFromString<JsonObject>(payload))
}.expectSuccess().apply {
val url = bodyAsText()
assert(url.contains("presentation_definition_uri="))
assert(!url.contains("presentation_definition="))
output?.invoke(bodyAsText())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"request_credentials": [
{
"credential": "OpenBadgeCredential",
"policies": [
{
"policy": "schema",
"args": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "object"
}
}
}
}
]
}
]
}

0 comments on commit fa39a60

Please sign in to comment.