diff --git a/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ESdJwtTest.kt b/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ESdJwtTest.kt new file mode 100644 index 000000000..fd1adbb0a --- /dev/null +++ b/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ESdJwtTest.kt @@ -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(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- + } +} \ No newline at end of file diff --git a/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ETest.kt b/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ETest.kt index a8c431b1d..b825cfa3f 100644 --- a/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ETest.kt +++ b/waltid-services/waltid-e2e-tests/src/test/kotlin/E2ETest.kt @@ -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.* @@ -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(issuerKey)) - put("issuerDid", issuerDid) - put("credentialConfigurationId", "OpenBadgeCredential_jwt_vc_json") - put("credentialData", Json.decodeFromString(openBadgeCredentialData)) - put("mapping", Json.decodeFromString(credentialMapping)) - } - private val sdjwtCredential = JsonObject( - jwtCredential.plus( - Pair(//todo: converted to map - "selectiveDisclosure", Json.decodeFromString(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(issuerKey)) + put("issuerDid", issuerDid) + put("credentialConfigurationId", "OpenBadgeCredential_jwt_vc_json") + put("credentialData", Json.decodeFromString(openBadgeCredentialData)) + put("mapping", Json.decodeFromString(credentialMapping)) + put("selectiveDisclosure", Json.decodeFromString(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) { @@ -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- @@ -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!" } @@ -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(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) { @@ -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) { diff --git a/waltid-services/waltid-e2e-tests/src/test/kotlin/ExchangeApi.kt b/waltid-services/waltid-e2e-tests/src/test/kotlin/ExchangeApi.kt index 3dd631de4..93a834795 100644 --- a/waltid-services/waltid-e2e-tests/src/test/kotlin/ExchangeApi.kt +++ b/waltid-services/waltid-e2e-tests/src/test/kotlin/ExchangeApi.kt @@ -85,18 +85,11 @@ class ExchangeApi(private val client: HttpClient) { suspend fun usePresentationRequest( wallet: UUID, - did: String? = null, - presentationRequestUrl: String, - credentialIds: List + 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() } } \ No newline at end of file diff --git a/waltid-services/waltid-e2e-tests/src/test/kotlin/VerifierApi.kt b/waltid-services/waltid-e2e-tests/src/test/kotlin/VerifierApi.kt index b066f86ad..746add70b 100644 --- a/waltid-services/waltid-e2e-tests/src/test/kotlin/VerifierApi.kt +++ b/waltid-services/waltid-e2e-tests/src/test/kotlin/VerifierApi.kt @@ -22,6 +22,9 @@ object Verifier { client.post("/openid4vc/verify") { setBody(Json.decodeFromString(payload)) }.expectSuccess().apply { + val url = bodyAsText() + assert(url.contains("presentation_definition_uri=")) + assert(!url.contains("presentation_definition=")) output?.invoke(bodyAsText()) } } diff --git a/waltid-services/waltid-e2e-tests/src/test/resources/presentation/openbadgecredential-name-field-presentation-request.json b/waltid-services/waltid-e2e-tests/src/test/resources/presentation/openbadgecredential-name-field-presentation-request.json new file mode 100644 index 000000000..7b9a0dd3a --- /dev/null +++ b/waltid-services/waltid-e2e-tests/src/test/resources/presentation/openbadgecredential-name-field-presentation-request.json @@ -0,0 +1,23 @@ +{ + "request_credentials": [ + { + "credential": "OpenBadgeCredential", + "policies": [ + { + "policy": "schema", + "args": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "object" + } + } + } + } + ] + } + ] +} \ No newline at end of file