From 53336b3accc8b65deca31208e3d92ca3c3884167 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Wed, 19 Feb 2025 14:28:13 +0200 Subject: [PATCH] Add functional tests for external-uuid-security --- .../functional/AuthenticationCacheSpec.kt | 189 +++++++++++++++++- .../PrebidCacheContainerConfig.kt | 8 +- 2 files changed, 191 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/org/prebid/cache/functional/AuthenticationCacheSpec.kt b/src/test/kotlin/org/prebid/cache/functional/AuthenticationCacheSpec.kt index 2149be3..e54c5ab 100644 --- a/src/test/kotlin/org/prebid/cache/functional/AuthenticationCacheSpec.kt +++ b/src/test/kotlin/org/prebid/cache/functional/AuthenticationCacheSpec.kt @@ -5,6 +5,7 @@ import io.kotest.assertions.throwables.shouldThrowExactly import io.kotest.core.spec.style.ShouldSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.beEmpty +import io.kotest.matchers.string.shouldContain import io.ktor.client.statement.bodyAsText import io.ktor.http.contentType import org.prebid.cache.functional.BaseSpec.Companion.prebidCacheConfig @@ -13,6 +14,7 @@ import org.prebid.cache.functional.model.request.RequestObject import org.prebid.cache.functional.model.request.TransferValue import org.prebid.cache.functional.service.ApiException import org.prebid.cache.functional.util.getRandomString +import org.prebid.cache.functional.util.getRandomUuid import org.springframework.http.HttpStatus.UNAUTHORIZED class AuthenticationCacheSpec : ShouldSpec({ @@ -102,7 +104,7 @@ class AuthenticationCacheSpec : ShouldSpec({ // when: POST cache endpoint is called val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject, apiKey = null) } - // then: Bad Request exception is thrown + // then: Unauthorized exception is thrown assertSoftly { exception.statusCode shouldBe UNAUTHORIZED.value() exception.responseBody should beEmpty() @@ -126,7 +128,7 @@ class AuthenticationCacheSpec : ShouldSpec({ val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject, apiKey = "") } - // then: Bad Request exception is thrown + // then: Unauthorized exception is thrown assertSoftly { exception.statusCode shouldBe UNAUTHORIZED.value() exception.responseBody should beEmpty() @@ -150,7 +152,7 @@ class AuthenticationCacheSpec : ShouldSpec({ val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject, apiKey = getRandomString()) } - // then: Bad Request exception is thrown + // then: Unauthorized exception is thrown assertSoftly { exception.statusCode shouldBe UNAUTHORIZED.value() exception.responseBody should beEmpty() @@ -175,10 +177,189 @@ class AuthenticationCacheSpec : ShouldSpec({ val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject, apiKey = prebidApiKey.uppercase()) } - // then: Bad Request exception is thrown + // then: Unauthorized exception is thrown assertSoftly { exception.statusCode shouldBe UNAUTHORIZED.value() exception.responseBody should beEmpty() } } + + should("should save JSON transfer value with proper api-key in header when cache-write-secured and external-uuid-secured are enabled") { + // given: Prebid Cache with external-uuid-secured=true property + val prebidApiKey = getRandomString() + val prebidCacheApi = BaseSpec.getPrebidCacheApi( + prebidCacheConfig.getBaseRedisConfig( + allowExternalUuid = true, + cacheWriteSecured = true, + externalUuidSecured = true, + apiKey = prebidApiKey + ) + ) + + // and: Request object with JSON transfer value + val requestObject = RequestObject.getDefaultJsonRequestObject() + val requestTransferValue = objectMapper.readValue(requestObject.puts[0].value, TransferValue::class.java) + + // when: POST cache endpoint is called + val postResponse = prebidCacheApi.postCache(requestObject, apiKey = prebidApiKey) + + // when: GET cache endpoint is called + val getCacheResponse = BaseSpec.getPrebidCacheApi().getCache(postResponse.responses[0].uuid) + + // then: response content type is the same as request object type + getCacheResponse.contentType()?.contentType shouldBe "application" + getCacheResponse.contentType()?.contentSubtype shouldBe requestObject.puts[0].type.getValue() + + // and: transfer value is returned + val responseTransferValue = objectMapper.readValue(getCacheResponse.bodyAsText(), TransferValue::class.java) + + assertSoftly { + responseTransferValue.adm shouldBe requestTransferValue.adm + responseTransferValue.width shouldBe requestTransferValue.width + responseTransferValue.height shouldBe requestTransferValue.height + } + } + + should("should save JSON transfer value with proper api-key in header when cache-write-secured disabled and external-uuid-secured enabled") { + // given: Prebid Cache with external-uuid-secured=true property + val prebidApiKey = getRandomString() + val prebidCacheApi = BaseSpec.getPrebidCacheApi( + prebidCacheConfig.getBaseRedisConfig( + allowExternalUuid = true, + cacheWriteSecured = false, + externalUuidSecured = true, + apiKey = prebidApiKey + ) + ) + + // and: Request object with JSON transfer value + val requestObject = RequestObject.getDefaultJsonRequestObject() + val requestTransferValue = objectMapper.readValue(requestObject.puts[0].value, TransferValue::class.java) + + // when: POST cache endpoint is called + val postResponse = prebidCacheApi.postCache(requestObject, apiKey = prebidApiKey) + + // when: GET cache endpoint is called + val getCacheResponse = BaseSpec.getPrebidCacheApi().getCache(postResponse.responses[0].uuid) + + // then: response content type is the same as request object type + getCacheResponse.contentType()?.contentType shouldBe "application" + getCacheResponse.contentType()?.contentSubtype shouldBe requestObject.puts[0].type.getValue() + + // and: transfer value is returned + val responseTransferValue = objectMapper.readValue(getCacheResponse.bodyAsText(), TransferValue::class.java) + + assertSoftly { + responseTransferValue.adm shouldBe requestTransferValue.adm + responseTransferValue.width shouldBe requestTransferValue.width + responseTransferValue.height shouldBe requestTransferValue.height + } + } + + should("should save JSON transfer value with proper api-key in header when cache-write-secured and external-uuid-secured are disabled") { + // given: Prebid Cache with external-uuid-secured=false property + val prebidCacheApi = BaseSpec.getPrebidCacheApi( + prebidCacheConfig.getBaseRedisConfig( + allowExternalUuid = true, + cacheWriteSecured = false, + externalUuidSecured = false, + apiKey = getRandomString() + ) + ) + + // and: Request object with JSON transfer value + val requestObject = RequestObject.getDefaultJsonRequestObject() + val requestTransferValue = objectMapper.readValue(requestObject.puts[0].value, TransferValue::class.java) + + // when: POST cache endpoint is called + val postResponse = prebidCacheApi.postCache(requestObject) + + // when: GET cache endpoint is called + val getCacheResponse = BaseSpec.getPrebidCacheApi().getCache(postResponse.responses[0].uuid) + + // then: response content type is the same as request object type + getCacheResponse.contentType()?.contentType shouldBe "application" + getCacheResponse.contentType()?.contentSubtype shouldBe requestObject.puts[0].type.getValue() + + // and: transfer value is returned + val responseTransferValue = objectMapper.readValue(getCacheResponse.bodyAsText(), TransferValue::class.java) + + assertSoftly { + responseTransferValue.adm shouldBe requestTransferValue.adm + responseTransferValue.width shouldBe requestTransferValue.width + responseTransferValue.height shouldBe requestTransferValue.height + } + } + + should("throw an exception when api key is missing for request cache_write_secured=true and external_uuid_Secured=true") { + // given: Prebid Cache with external-uuid-secured=true property + val prebidCacheApi = BaseSpec.getPrebidCacheApi( + prebidCacheConfig.getBaseRedisConfig( + allowExternalUuid = true, + cacheWriteSecured = true, + externalUuidSecured = true, + apiKey = getRandomString() + ) + ) + + // and: Request object with set payload transfer key + val requestObject = RequestObject.getDefaultJsonRequestObject().apply { puts[0].key = getRandomUuid() } + + // when: POST cache endpoint is called + val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject) } + + // then: Unauthorized exception is thrown + assertSoftly { + exception.statusCode shouldBe UNAUTHORIZED.value() + exception.responseBody should beEmpty() + } + } + + should("throw an exception when api key is missing for request and external_uuid_Secured=true") { + // given: Prebid Cache with external-uuid-secured=true property + val prebidCacheApi = BaseSpec.getPrebidCacheApi( + prebidCacheConfig.getBaseRedisConfig( + allowExternalUuid = true, + cacheWriteSecured = false, + externalUuidSecured = true, + apiKey = getRandomString() + ) + ) + + // and: Request object with set payload transfer key + val requestObject = RequestObject.getDefaultJsonRequestObject().apply { puts[0].key = getRandomUuid() } + + // when: POST cache endpoint is called + val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject) } + + // then: Unauthorized exception is thrown + assertSoftly { + exception.statusCode shouldBe UNAUTHORIZED.value() + exception.responseBody shouldContain "\"message\":\"Prebid cache host forbids specifying UUID in request by unauthorized users.\"" + } + } + + should("throw an exception when api key is mismatched for request and external-uuid-secured=true") { + // given: Prebid Cache with external-uuid-secured=true property + val prebidCacheApi = BaseSpec.getPrebidCacheApi( + prebidCacheConfig.getBaseRedisConfig( + allowExternalUuid = true, + cacheWriteSecured = false, + externalUuidSecured = true, + apiKey = getRandomString() + ) + ) + + // and: Request object with set payload transfer key + val requestObject = RequestObject.getDefaultJsonRequestObject().apply { puts[0].key = getRandomUuid() } + + // when: POST cache endpoint is called + val exception = shouldThrowExactly { prebidCacheApi.postCache(requestObject, apiKey = getRandomString()) } + + // then: Unauthorized exception is thrown + assertSoftly { + exception.statusCode shouldBe UNAUTHORIZED.value() + exception.responseBody shouldContain "\"message\":\"Prebid cache host forbids specifying UUID in request by unauthorized users.\"" + } + } }) diff --git a/src/test/kotlin/org/prebid/cache/functional/testcontainers/PrebidCacheContainerConfig.kt b/src/test/kotlin/org/prebid/cache/functional/testcontainers/PrebidCacheContainerConfig.kt index 8a3c79a..c18b5a2 100644 --- a/src/test/kotlin/org/prebid/cache/functional/testcontainers/PrebidCacheContainerConfig.kt +++ b/src/test/kotlin/org/prebid/cache/functional/testcontainers/PrebidCacheContainerConfig.kt @@ -16,9 +16,10 @@ class PrebidCacheContainerConfig( fun getBaseRedisConfig( allowExternalUuid: Boolean, cacheWriteSecured: Boolean = false, + externalUuidSecured: Boolean = false, apiKey: String? = null ): Map = - getBaseConfig(allowExternalUuid, cacheWriteSecured, apiKey) + getRedisConfig() + getBaseConfig(allowExternalUuid, cacheWriteSecured, apiKey, externalUuidSecured) + getRedisConfig() fun getBaseAerospikeConfig( allowExternalUuid: Boolean, @@ -104,13 +105,14 @@ class PrebidCacheContainerConfig( allowExternalUuid: Boolean, cacheWriteSecured: Boolean = false, apiKey: String? = null, + externalUuidSecured: Boolean? = null, endpoint: String = "/storage" ): Map = getCachePrefixConfig() + getCacheExpiryConfig() + getAllowExternalUuidConfig(allowExternalUuid) + getCacheTimeoutConfig("2500") + - getApiConfig(endpoint, apiKey, cacheWriteSecured) + getApiConfig(endpoint, apiKey, externalUuidSecured, cacheWriteSecured) private fun getCachePrefixConfig(): Map = mapOf("cache.prefix" to "prebid_") @@ -120,10 +122,12 @@ class PrebidCacheContainerConfig( private fun getApiConfig( endpoint: String, apiKey: String? = null, + externalUuidSecured: Boolean? = null, allowPublicWrite: Boolean? = null ): Map = buildMap { put("api.storage-path", endpoint) apiKey?.let { put("api.api-key", it) } + externalUuidSecured?.let { put("api.external-u-u-i-d-secured", it.toString()) } allowPublicWrite?.let { put("api.cache-write-secured", it.toString()) } } }