diff --git a/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java b/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java index 09ca1976eb2..acc56169962 100644 --- a/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java @@ -15,7 +15,9 @@ import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import io.vertx.ext.web.RoutingContext; +import lombok.Value; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.DebugResolver; @@ -167,8 +169,10 @@ private Future parseBidRequest(HttpRequestContext httpRequest, Aucti return Future.failedFuture(new InvalidRequestException("AMP requests require an AMP tag_id")); } + final ConsentParam consentParam = consentParamFromQueryStringParams(httpRequest); + validateConsentParam(consentParam, auctionContext.getPrebidErrors()); + final ConsentType consentType = consentTypeFromQueryStringParams(httpRequest); - final String consentString = consentStringFromQueryStringParams(httpRequest); final String addtlConsent = addtlConsentFromQueryStringParams(httpRequest); final Integer gdpr = gdprFromQueryStringParams(httpRequest); final Integer debug = debugFromQueryStringParam(httpRequest); @@ -176,8 +180,8 @@ private Future parseBidRequest(HttpRequestContext httpRequest, Aucti final BidRequest bidRequest = BidRequest.builder() .site(createSite(httpRequest)) - .user(createUser(consentType, consentString, addtlConsent)) - .regs(createRegs(consentString, consentType, gdpr)) + .user(createUser(consentType, consentParam, addtlConsent)) + .regs(createRegs(consentParam, consentType, gdpr)) .test(debug) .tmax(timeout) .ext(createExt(httpRequest, tagId, debug)) @@ -186,6 +190,19 @@ private Future parseBidRequest(HttpRequestContext httpRequest, Aucti return Future.succeededFuture(bidRequest); } + private void validateConsentParam(ConsentParam consentParam, List errors) { + if (consentParam == null) { + return; + } + + if (!consentParam.getCcpa() && !consentParam.getTcfV2()) { + errors.add(String.format( + "Amp request parameter %s has invalid format: %s", + consentParam.getFromParam(), + consentParam.getConsentString())); + } + } + private static Site createSite(HttpRequestContext httpRequest) { final String accountId = StringUtils.trimToNull(httpRequest.getQueryParams().get(ACCOUNT_REQUEST_PARAM)); final String canonicalUrl = StringUtils.trimToNull(canonicalUrl(httpRequest)); @@ -200,15 +217,14 @@ private static Site createSite(HttpRequestContext httpRequest) { : null; } - private static User createUser(ConsentType consentType, String consentString, String addtlConsent) { - final boolean tcfV2ConsentProvided = StringUtils.isNotBlank(consentString) - && TcfDefinerService.isConsentStringValid(consentString) + private static User createUser(ConsentType consentType, ConsentParam consentParam, String addtlConsent) { + final boolean shouldSetUserConsent = consentParam != null && BooleanUtils.isTrue(consentParam.getTcfV2()) && (consentType == null || consentType == ConsentType.tcfV2); - if (StringUtils.isNotBlank(addtlConsent) || tcfV2ConsentProvided) { + if (StringUtils.isNotBlank(addtlConsent) || shouldSetUserConsent) { final ExtUser.ExtUserBuilder userExtBuilder = ExtUser.builder(); - if (tcfV2ConsentProvided) { - userExtBuilder.consent(consentString); + if (shouldSetUserConsent) { + userExtBuilder.consent(consentParam.getConsentString()); } if (StringUtils.isNotBlank(addtlConsent)) { userExtBuilder.consentedProvidersSettings(ConsentedProvidersSettings.of(addtlConsent)); @@ -219,11 +235,11 @@ private static User createUser(ConsentType consentType, String consentString, St return null; } - private static Regs createRegs(String consentString, ConsentType consentType, Integer gdpr) { - final boolean ccpaProvided = Ccpa.isValid(consentString) + private static Regs createRegs(ConsentParam consentParam, ConsentType consentType, Integer gdpr) { + final boolean shouldSetUsPrivacy = consentParam != null && BooleanUtils.isTrue(consentParam.getCcpa()) && (consentType == null || consentType == ConsentType.usPrivacy); - if (ccpaProvided || gdpr != null) { - return Regs.of(null, ExtRegs.of(gdpr, ccpaProvided ? consentString : null)); + if (shouldSetUsPrivacy || gdpr != null) { + return Regs.of(null, ExtRegs.of(gdpr, shouldSetUsPrivacy ? consentParam.getConsentString() : null)); } return null; @@ -270,11 +286,24 @@ private static ConsentType consentTypeFromQueryStringParams(HttpRequestContext h } } - private static String consentStringFromQueryStringParams(HttpRequestContext httpRequest) { - final String requestConsentParam = httpRequest.getQueryParams().get(CONSENT_PARAM); - final String requestGdprConsentParam = httpRequest.getQueryParams().get(GDPR_CONSENT_PARAM); + private static ConsentParam consentParamFromQueryStringParams(HttpRequestContext httpRequest) { + final String consentParam = httpRequest.getQueryParams().get(CONSENT_PARAM); + if (consentParam != null) { + return ConsentParam.of(consentParam, + CONSENT_PARAM, + TcfDefinerService.isConsentStringValid(consentParam), + Ccpa.isValid(consentParam)); + } + + final String gdprConsentParam = httpRequest.getQueryParams().get(GDPR_CONSENT_PARAM); + if (gdprConsentParam != null) { + return ConsentParam.of(gdprConsentParam, + GDPR_CONSENT_PARAM, + TcfDefinerService.isConsentStringValid(gdprConsentParam), + Ccpa.isValid(gdprConsentParam)); + } - return ObjectUtils.firstNonNull(requestConsentParam, requestGdprConsentParam); + return null; } private static String addtlConsentFromQueryStringParams(HttpRequestContext httpRequest) { @@ -708,4 +737,16 @@ private ExtRequestTargeting createTargetingWithDefaults(ExtRequestPrebid prebid) .includeformat(includeFormat) .build(); } + + @Value(staticConstructor = "of") + private static class ConsentParam { + + String consentString; + + String fromParam; + + Boolean tcfV2; + + Boolean ccpa; + } } diff --git a/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java index 3264e007c54..fc87ce71a20 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java @@ -1402,6 +1402,48 @@ public void shouldReturnBidRequestWithRegsContainsGdprEqualOneIfGdprAppliesIsTru .isEqualTo(Regs.of(null, ExtRegs.of(1, null))); } + @Test + public void shouldNotAddErrorToAuctionContextIfNoConsentParamProvided() { + // given + givenBidRequest(); + + // when + final AuctionContext result = target.fromRequest(routingContext, 0L).result(); + + // then + assertThat(result.getPrebidErrors()).isEmpty(); + } + + @Test + public void shouldAddErrorToAuctionContextWhenConsentStringQueryParamIsInvalid() { + // given + routingContext.queryParams().add("consent_string", "consent-value"); + + givenBidRequest(); + + // when + final AuctionContext result = target.fromRequest(routingContext, 0L).result(); + + // then + assertThat(result.getPrebidErrors()) + .containsExactly("Amp request parameter consent_string has invalid format: consent-value"); + } + + @Test + public void shouldAddErrorToAuctionContextWhenGdprConsentQueryParamIsInvalid() { + // given + routingContext.queryParams().add("gdpr_consent", "consent-value"); + + givenBidRequest(); + + // when + final AuctionContext result = target.fromRequest(routingContext, 0L).result(); + + // then + assertThat(result.getPrebidErrors()) + .containsExactly("Amp request parameter gdpr_consent has invalid format: consent-value"); + } + @Test public void shouldReturnBidRequestWithRegsContainsGdprEqualZeroIfGdprAppliesIsFalse() { // given