Skip to content
Merged
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
@@ -1,6 +1,12 @@
package org.prebid.server.functional.tests.privacy

import io.netty.handler.codec.http.HttpResponseStatus
import org.prebid.server.functional.model.config.AccountConfig
import org.prebid.server.functional.model.config.AccountGdprConfig
import org.prebid.server.functional.model.config.AccountPrivacyConfig
import org.prebid.server.functional.model.config.PurposeConfig
import org.prebid.server.functional.model.db.Account
import org.prebid.server.functional.model.request.GppSectionId
import org.prebid.server.functional.model.request.cookiesync.CookieSyncRequest
import org.prebid.server.functional.model.response.cookiesync.UserSyncInfo
import org.prebid.server.functional.service.PrebidServerException
Expand All @@ -13,7 +19,10 @@ import org.prebid.server.functional.util.privacy.TcfConsent
import org.prebid.server.functional.util.privacy.gpp.TcfEuV2Consent
import org.prebid.server.functional.util.privacy.gpp.UsV1Consent

import static org.prebid.server.functional.model.bidder.BidderName.ALIAS
import static org.prebid.server.functional.model.bidder.BidderName.GENERIC
import static org.prebid.server.functional.model.config.Purpose.P1
import static org.prebid.server.functional.model.config.PurposeEnforcement.NO
import static org.prebid.server.functional.model.request.GppSectionId.TCF_EU_V2
import static org.prebid.server.functional.model.request.GppSectionId.USP_V1
import static org.prebid.server.functional.model.response.cookiesync.UserSyncInfo.Type.IFRAME
Expand All @@ -28,15 +37,26 @@ class GppCookieSyncSpec extends BaseSpec {
private static final UserSyncInfo.Type USER_SYNC_TYPE = REDIRECT
private static final boolean CORS_SUPPORT = false
private static final String USER_SYNC_URL = "$networkServiceContainer.rootUri/generic-usersync"
private static final GppSectionId FIRST_GPP_SECTION = PBSUtils.getRandomEnum(GppSectionId.class)
private static final GppSectionId SECOND_GPP_SECTION = PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION])

private static final Map<String, String> GENERIC_CONFIG = [
"adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String,
"adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.url" : USER_SYNC_URL,
"adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.support-cors": CORS_SUPPORT.toString()]
private static final Map<String, String> GENERIC_WITH_SKIP_CONFIG = [
"adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String,
"adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.url" : "$networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(),
"adapters.${GENERIC.value}.usersync.skipwhen.gdpr" : 'true',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the case when gdpr: false?

"adapters.${GENERIC.value}.usersync.skipwhen.gpp_sid" : "${FIRST_GPP_SECTION.value}, ${SECOND_GPP_SECTION.value}".toString(),
"adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.support-cors": CORS_SUPPORT.toString()]

private static PrebidServerService prebidServerService = pbsServiceFactory.getService(GENERIC_CONFIG)
private static PrebidServerService prebidServerServiceWithSkipConfig = pbsServiceFactory.getService(GENERIC_WITH_SKIP_CONFIG + GENERIC_ALIAS_CONFIG)

def cleanupSpec() {
pbsServiceFactory.removeContainer(GENERIC_CONFIG)
pbsServiceFactory.removeContainer(GENERIC_WITH_SKIP_CONFIG + GENERIC_ALIAS_CONFIG)
}

def "PBS cookie sync request should set GDPR to 1 when gpp_sid contains 2"() {
Expand Down Expand Up @@ -171,8 +191,8 @@ class GppCookieSyncSpec extends BaseSpec {
it.gpp = new TcfEuV2Consent.Builder().build()
it.gdpr = null
it.gdprConsent = new TcfConsent.Builder().setPurposesLITransparency(DEVICE_ACCESS)
.setVendorLegitimateInterest([GENERIC_VENDOR_ID])
.build()
.setVendorLegitimateInterest([GENERIC_VENDOR_ID])
.build()
}

when: "PBS processes cookie sync request"
Expand Down Expand Up @@ -261,4 +281,236 @@ class GppCookieSyncSpec extends BaseSpec {
where:
userSyncFormat << [REDIRECT, IFRAME]
}

def "PBS should emit proper error message when request contain gdpr config and global skip gdpr config for adapter"() {
given: "Default CookieSyncRequest with gdpr config"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = TCF_EU_V2.intValue
it.gdpr = 1
it.gdprConsent = new TcfConsent.Builder().build()
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response userSync url shouldn't contain cookies and userSync"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert !bidderStatus.userSync
assert !bidderStatus.noCookie

and: "Response should contain proper error message"
assert bidderStatus.error == "Rejected by regulation scope"
}

def "PBS should emit proper error message when alias request contain gdpr config and global skip gdpr config for adapter"() {
given: "Default CookieSyncRequest with gdpr config"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.bidders = [ALIAS]
it.gppSid = TCF_EU_V2.intValue
it.gdpr = 1
it.gdprConsent = new TcfConsent.Builder().build()
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response userSync url shouldn't contain cookies and userSync"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert !bidderStatus.userSync
assert !bidderStatus.noCookie

and: "Response should contain proper error message"
assert bidderStatus.error == "Rejected by regulation scope"
}

def "PBS should emit proper error message when request contain gpp config and specific global skip gpp config for adapter"() {
given: "Default CookieSyncRequest with gpp and gppSid"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = TCF_EU_V2.intValue
it.gpp = new UsV1Consent.Builder().build()
it.gppSid = gppSid
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response userSync url shouldn't contain cookies and userSync"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert !bidderStatus.userSync
assert !bidderStatus.noCookie

and: "Response should contain proper error message"
assert bidderStatus.error == "Rejected by regulation scope"

where:
gppSid << ["${FIRST_GPP_SECTION.value}",
"${SECOND_GPP_SECTION.value}",
"${FIRST_GPP_SECTION.value}, ${SECOND_GPP_SECTION.value}",
"${SECOND_GPP_SECTION.value}, ${FIRST_GPP_SECTION.value}",
"${SECOND_GPP_SECTION.value}, ${FIRST_GPP_SECTION.value}",
"${PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION, SECOND_GPP_SECTION]).value}, ${SECOND_GPP_SECTION.value}",
"${FIRST_GPP_SECTION.value}, ${PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION, SECOND_GPP_SECTION]).value}"
]
}

def "PBS should emit proper error message when alias request contain gpp config and specific global skip gpp config for adapter"() {
Comment thread
marki1an marked this conversation as resolved.
given: "Default CookieSyncRequest with gpp and gppSid"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = TCF_EU_V2.intValue
it.bidders = [ALIAS]
it.gpp = new UsV1Consent.Builder().build()
it.gppSid = gppSid
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response userSync url shouldn't contain cookies and userSync"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert !bidderStatus.userSync
assert !bidderStatus.noCookie

and: "Response should contain proper error message"
assert bidderStatus.error == "Rejected by regulation scope"

where:
gppSid << ["${FIRST_GPP_SECTION.value}",
"${SECOND_GPP_SECTION.value}",
"${FIRST_GPP_SECTION.value}, ${SECOND_GPP_SECTION.value}",
"${SECOND_GPP_SECTION.value}, ${FIRST_GPP_SECTION.value}",
"${SECOND_GPP_SECTION.value}, ${FIRST_GPP_SECTION.value}",
"${PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION, SECOND_GPP_SECTION]).value}, ${SECOND_GPP_SECTION.value}",
"${FIRST_GPP_SECTION.value}, ${PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION, SECOND_GPP_SECTION]).value}"
]
}

def "PBS shouldn't emit error message when request doesn't contain gdpr config and global skip gdpr config for adapter"() {
given: "Default CookieSyncRequest with gdpr config"
def gppSid = TCF_EU_V2
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = gppSid.intValue
it.gdpr = 0
it.gdprConsent = new TcfConsent.Builder().build()
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response userSync url should contain cookies and userSync"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert HttpUtil.findUrlParameterValue(bidderStatus.userSync?.url, "gpp") == ""
assert HttpUtil.findUrlParameterValue(bidderStatus.userSync?.url, "gpp_sid") == gppSid.value

and: "Response shouldn't contains any error"
assert !bidderStatus.error
}

def "PBS shouldn't emit error message when request does contain gdpr config and global skip gdpr config disabled for adapter"() {
given: "Pbs config with usersync.#userSyncFormat.url"
def pbsConfig = [
"adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String,
"adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.url" : USER_SYNC_URL,
"adapters.${GENERIC.value}.usersync.skipwhen.gdpr" : 'false',
"adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.support-cors": CORS_SUPPORT.toString()]
def prebidServerService = pbsServiceFactory.getService(pbsConfig)

and: "Default CookieSyncRequest with gdpr config"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = TCF_EU_V2
it.gdpr = 1
it.gdprConsent = new TcfConsent.Builder().setPurposesLITransparency(DEVICE_ACCESS)
.setVendorLegitimateInterest([GENERIC_VENDOR_ID])
.build()
it.account = PBSUtils.randomNumber
}

and: "Save account config with requireConsent into DB"
def purposes = [(P1): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
def privacyConfig = new AccountPrivacyConfig(gdpr: accountGdprConfig)
def account = new Account(uuid: cookieSyncRequest.account, config: new AccountConfig(privacy: privacyConfig))
accountDao.save(account)

when: "PBS processes cookie sync request"
def response = prebidServerService.sendCookieSyncRequest(cookieSyncRequest)

then: "Response should contain proper userSync url"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert bidderStatus.userSync?.url == USER_SYNC_URL

and: "Response shouldn't contains any error"
assert !bidderStatus.error

cleanup: "Stop and remove pbs container"
pbsServiceFactory.removeContainer(pbsConfig)
}

def "PBS shouldn't emit error message when request does contain gdpr config and global skip gdpr config default for adapter"() {
given: "Default CookieSyncRequest with gdpr config"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = TCF_EU_V2
it.gdpr = 1
it.gdprConsent = new TcfConsent.Builder().setPurposesLITransparency(DEVICE_ACCESS)
.setVendorLegitimateInterest([GENERIC_VENDOR_ID])
.build()
it.account = PBSUtils.randomNumber
}

and: "Save account config with requireConsent into DB"
def purposes = [(P1): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
def privacyConfig = new AccountPrivacyConfig(gdpr: accountGdprConfig)
def account = new Account(uuid: cookieSyncRequest.account, config: new AccountConfig(privacy: privacyConfig))
accountDao.save(account)

when: "PBS processes cookie sync request"
def response = prebidServerService.sendCookieSyncRequest(cookieSyncRequest)

then: "Response should contain proper userSync url"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert bidderStatus.userSync?.url == USER_SYNC_URL

and: "Response shouldn't contains any error"
assert !bidderStatus.error
}

def "PBS shouldn't emit error message when request doesn't contain matched gpp config and specific global skip gpp config for adapter"() {
given: "Default CookieSyncRequest with gpp and gppSid"
def gpp = new UsV1Consent.Builder().build()
def gppSid = "${PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION, SECOND_GPP_SECTION]).value}"
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = TCF_EU_V2.intValue
it.gpp = gpp
Comment thread
marki1an marked this conversation as resolved.
it.gppSid = gppSid
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response userSync url should contain gpp and gppSid"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert HttpUtil.findUrlParameterValue(bidderStatus.userSync?.url, "gpp") == gpp.toString()
assert HttpUtil.findUrlParameterValue(bidderStatus.userSync?.url, "gpp_sid") == gppSid

and: "Response shouldn't contains any error"
assert !bidderStatus.error
}

def "PBS should also include validation warning when request matches skip config and has validation issue at same time"() {
def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap {
it.gppSid = PBSUtils.getRandomNumberWithExclusion(TCF_EU_V2.intValue)
it.gdpr = 1
it.gdprConsent = new TcfConsent.Builder().build()
}

when: "PBS processes cookie sync request"
def response = prebidServerServiceWithSkipConfig.sendCookieSyncRequest(cookieSyncRequest)

then: "Response should contain a warning"
assert response.warnings == ["GPP scope does not match TCF2 scope"]

then: "Privacy for bidder should be enforced"
def bidderStatus = response.getBidderUserSync(GENERIC)
assert bidderStatus.error == "Rejected by regulation scope"
}
}