Skip to content

Commit

Permalink
Add invalid ccpa notification in debug error response (#787)
Browse files Browse the repository at this point in the history
  • Loading branch information
DGarbar authored Dec 11, 2020
1 parent 7867aee commit 783670e
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,7 @@ private static Integer debugFromQueryStringParam(RoutingContext context) {
/**
* Extracts parameters from http request and overrides corresponding attributes in {@link BidRequest}.
*/
private BidRequest overrideParameters(BidRequest bidRequest, HttpServerRequest request,
List<String> errors) {
private BidRequest overrideParameters(BidRequest bidRequest, HttpServerRequest request, List<String> errors) {
final String requestConsentParam = request.getParam(CONSENT_PARAM);
final String requestGdprConsentParam = request.getParam(GDPR_CONSENT_PARAM);
final String consentString = ObjectUtils.firstNonNull(requestConsentParam, requestGdprConsentParam);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ Future<AuctionContext> toAuctionContext(RoutingContext routingContext,

return accountFrom(bidRequest, timeout, routingContext)
.compose(account -> privacyEnforcementService.contextFromBidRequest(
bidRequest, account, requestTypeMetric, timeout)
bidRequest, account, requestTypeMetric, timeout, errors)
.map(privacyContext -> AuctionContext.builder()
.routingContext(routingContext)
.uidsCookie(uidsCookieService.parseFromRequest(routingContext))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ public PrivacyEnforcementService(BidderCatalog bidderCatalog,
}

Future<PrivacyContext> contextFromBidRequest(
BidRequest bidRequest, Account account, MetricName requestType, Timeout timeout) {
BidRequest bidRequest, Account account, MetricName requestType, Timeout timeout, List<String> errors) {

final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest);
final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest, errors);

final Device device = bidRequest.getDevice();
final String ipAddress = device != null ? device.getIp() : null;
Expand Down
34 changes: 22 additions & 12 deletions src/main/java/org/prebid/server/privacy/PrivacyExtractor.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.prebid.server.proto.request.CookieSyncRequest;
import org.prebid.server.proto.request.PreBidRequest;

import java.util.List;

/**
* GDPR-aware utilities
*/
Expand All @@ -40,12 +42,13 @@ public class PrivacyExtractor {
* </ul><p>
* And construct {@link Privacy} from them. Use default values in case of invalid value.
*/
public Privacy validPrivacyFrom(BidRequest bidRequest) {
return extractPrivacy(bidRequest.getRegs(), bidRequest.getUser());
public Privacy validPrivacyFrom(BidRequest bidRequest, List<String> errors) {
return extractPrivacy(bidRequest.getRegs(), bidRequest.getUser(), errors);
}

@Deprecated
public Privacy validPrivacyFrom(PreBidRequest preBidRequest) {
return extractPrivacy(preBidRequest.getRegs(), preBidRequest.getUser());
return extractPrivacy(preBidRequest.getRegs(), preBidRequest.getUser(), null);
}

public Privacy validPrivacyFrom(CookieSyncRequest request) {
Expand All @@ -54,17 +57,17 @@ public Privacy validPrivacyFrom(CookieSyncRequest request) {
final String gdprConsent = request.getGdprConsent();
final String usPrivacy = request.getUsPrivacy();

return toValidPrivacy(gdpr, gdprConsent, usPrivacy, null);
return toValidPrivacy(gdpr, gdprConsent, usPrivacy, null, null);
}

public Privacy validPrivacyFromSetuidRequest(HttpServerRequest request) {
final String gdpr = request.getParam(SETUID_GDPR_PARAM);
final String gdprConsent = request.getParam(SETUID_GDPR_CONSENT_PARAM);

return toValidPrivacy(gdpr, gdprConsent, null, null);
return toValidPrivacy(gdpr, gdprConsent, null, null, null);
}

private Privacy extractPrivacy(Regs regs, User user) {
private Privacy extractPrivacy(Regs regs, User user, List<String> errors) {
final ExtRegs extRegs = regs != null ? regs.getExt() : null;
final ExtUser extUser = user != null ? user.getExt() : null;

Expand All @@ -74,27 +77,34 @@ private Privacy extractPrivacy(Regs regs, User user) {
final String usPrivacy = extRegs != null ? extRegs.getUsPrivacy() : null;
final Integer coppa = regs != null ? regs.getCoppa() : null;

return toValidPrivacy(gdpr, consent, usPrivacy, coppa);
return toValidPrivacy(gdpr, consent, usPrivacy, coppa, errors);
}

private static Privacy toValidPrivacy(String gdpr, String consent, String usPrivacy, Integer coppa) {
private static Privacy toValidPrivacy(String gdpr,
String consent,
String usPrivacy,
Integer coppa,
List<String> errors) {
final String validGdpr = ObjectUtils.notEqual(gdpr, "1") && ObjectUtils.notEqual(gdpr, "0")
? DEFAULT_GDPR_VALUE
: gdpr;
final String validConsent = consent == null ? DEFAULT_CONSENT_VALUE : consent;
final Ccpa validCcpa = usPrivacy == null ? DEFAULT_CCPA_VALUE : toValidCcpa(usPrivacy);
final Ccpa validCcpa = usPrivacy == null ? DEFAULT_CCPA_VALUE : toValidCcpa(usPrivacy, errors);
final Integer validCoppa = coppa == null ? DEFAULT_COPPA_VALUE : coppa;

return Privacy.of(validGdpr, validConsent, validCcpa, validCoppa);
}

private static Ccpa toValidCcpa(String usPrivacy) {
private static Ccpa toValidCcpa(String usPrivacy, List<String> errors) {
try {
Ccpa.validateUsPrivacy(usPrivacy);
return Ccpa.of(usPrivacy);
} catch (PreBidException e) {
// TODO add error to PBS response, not only in logs (See PR #758)
logger.debug("CCPA consent {0} has invalid format: {1}", usPrivacy, e.getMessage());
final String message = String.format("CCPA consent %s has invalid format: %s", usPrivacy, e.getMessage());
logger.debug(message);
if (errors != null) {
errors.add(message);
}
return DEFAULT_CCPA_VALUE;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public void setUp() {
given(timeoutResolver.resolve(any())).willReturn(2000L);
given(timeoutResolver.adjustTimeout(anyLong())).willReturn(1900L);

given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any()))
given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any(), any()))
.willReturn(Future.succeededFuture(PrivacyContext.of(
Privacy.of("0", EMPTY, Ccpa.EMPTY, 0),
TcfContext.empty())));
Expand Down Expand Up @@ -1693,7 +1693,7 @@ public void shouldEnrichRequestWithIpAddressAndCountryAndSaveAuctionContext() {
.geoInfo(GeoInfo.builder().vendor("v").country("ua").build())
.build(),
"ip");
given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any()))
given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any(), any()))
.willReturn(Future.succeededFuture(privacyContext));

// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -131,7 +132,7 @@ public void contextFromBidRequestShouldReturnCoppaContext() {

// when
final Future<PrivacyContext> privacyContext = privacyEnforcementService.contextFromBidRequest(
bidRequest, Account.empty("account"), null, null);
bidRequest, Account.empty("account"), null, null, new ArrayList<>());

// then
FutureAssertion.assertThat(privacyContext).succeededWith(
Expand Down Expand Up @@ -165,7 +166,7 @@ public void contextFromBidRequestShouldReturnTcfContext() {

// when
final Future<PrivacyContext> privacyContext = privacyEnforcementService.contextFromBidRequest(
bidRequest, Account.empty(accountId), requestType, null);
bidRequest, Account.empty(accountId), requestType, null, new ArrayList<>());

// then
final Privacy privacy = Privacy.of("1", "consent", Ccpa.of("1YYY"), 0);
Expand Down Expand Up @@ -207,7 +208,7 @@ public void contextFromBidRequestShouldReturnTcfContextWithIpMasked() {

// when
final Future<PrivacyContext> privacyContext = privacyEnforcementService.contextFromBidRequest(
bidRequest, Account.empty("account"), MetricName.openrtb2web, null);
bidRequest, Account.empty("account"), MetricName.openrtb2web, null, new ArrayList<>());

// then
final Privacy privacy = Privacy.of("1", "consent", Ccpa.of("1YYY"), 0);
Expand Down
48 changes: 33 additions & 15 deletions src/test/java/org/prebid/server/privacy/PrivacyExtractorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.prebid.server.proto.request.CookieSyncRequest;
import org.prebid.server.proto.request.PreBidRequest;

import java.util.ArrayList;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
Expand All @@ -37,7 +39,8 @@ public void setUp() {
@Test
public void shouldReturnGdprEmptyValueWhenRegsIsNull() {
// given and when
final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().build()).getGdpr();
final String gdpr =
privacyExtractor.validPrivacyFrom(BidRequest.builder().build(), new ArrayList<>()).getGdpr();

// then
assertThat(gdpr).isEmpty();
Expand All @@ -47,7 +50,7 @@ public void shouldReturnGdprEmptyValueWhenRegsIsNull() {
public void shouldReturnGdprEmptyValueWhenRegsExtIsNull() {
// given and when
final String gdpr = privacyExtractor.validPrivacyFrom(
BidRequest.builder().regs(Regs.of(null, null)).build())
BidRequest.builder().regs(Regs.of(null, null)).build(), new ArrayList<>())
.getGdpr();

// then
Expand All @@ -60,7 +63,8 @@ public void shouldReturnGdprEmptyValueWhenRegsExtGdprIsNoEqualsToOneOrZero() {
final Regs regs = Regs.of(null, ExtRegs.of(2, null));

// when
final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getGdpr();
final String gdpr =
privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()).getGdpr();

// then
assertThat(gdpr).isEmpty();
Expand All @@ -72,7 +76,8 @@ public void shouldReturnGdprOneWhenExtRegsContainsGdprOne() {
final Regs regs = Regs.of(null, ExtRegs.of(1, null));

// when
final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getGdpr();
final String gdpr =
privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()).getGdpr();

// then
assertThat(gdpr).isEqualTo("1");
Expand All @@ -84,7 +89,8 @@ public void shouldReturnGdprZeroWhenExtRegsContainsGdprZero() {
final Regs regs = Regs.of(null, ExtRegs.of(0, null));

// when
final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getGdpr();
final String gdpr =
privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()).getGdpr();

// then
assertThat(gdpr).isEqualTo("0");
Expand All @@ -93,7 +99,8 @@ public void shouldReturnGdprZeroWhenExtRegsContainsGdprZero() {
@Test
public void shouldReturnConsentEmptyValueWhenExtUserIsNull() {
// given and when
final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().build()).getConsentString();
final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().build(), new ArrayList<>())
.getConsentString();

// then
assertThat(consent).isEmpty();
Expand All @@ -105,8 +112,9 @@ public void shouldReturnConsentEmptyValueWhenUserConsentIsNull() {
final User user = User.builder().ext(ExtUser.builder().build()).build();

// when
final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build())
.getConsentString();
final String consent =
privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build(), new ArrayList<>())
.getConsentString();

// then
assertThat(consent).isEmpty();
Expand All @@ -118,23 +126,28 @@ public void shouldReturnConsentWhenUserContainsConsent() {
final User user = User.builder().ext(ExtUser.builder().consent("consent").build()).build();

// when
final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build())
.getConsentString();
final String consent =
privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build(), new ArrayList<>())
.getConsentString();

// then
assertThat(consent).isEqualTo("consent");
}

@Test
public void shouldReturnDefaultCcpaIfNotValid() {
public void shouldReturnDefaultCcpaWhenNotValidAndAddError() {
// given
final Regs regs = Regs.of(null, ExtRegs.of(null, "invalid"));
final ArrayList<String> errors = new ArrayList<>();

// when
final Ccpa ccpa = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getCcpa();
final Ccpa ccpa =
privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), errors).getCcpa();

// then
assertThat(ccpa).isEqualTo(Ccpa.EMPTY);
assertThat(errors).containsOnly(
"CCPA consent invalid has invalid format: us_privacy must contain 4 characters");
}

@Test
Expand All @@ -143,7 +156,9 @@ public void shouldReturnDefaultCoppaIfNull() {
final Regs regs = Regs.of(null, null);

// when
final Integer coppa = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getCoppa();
final Integer coppa =
privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>())
.getCoppa();

// then
assertThat(coppa).isZero();
Expand All @@ -155,7 +170,9 @@ public void shouldReturnCoppaIfNotNull() {
final Regs regs = Regs.of(42, null);

// when
final Integer coppa = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getCoppa();
final Integer coppa =
privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>())
.getCoppa();

// then
assertThat(coppa).isEqualTo(42);
Expand All @@ -168,7 +185,8 @@ public void shouldReturnPrivacyWithParametersExtractedFromBidRequest() {
final User user = User.builder().ext(ExtUser.builder().consent("consent").build()).build();

// when
final Privacy privacy = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).user(user).build());
final Privacy privacy = privacyExtractor
.validPrivacyFrom(BidRequest.builder().regs(regs).user(user).build(), new ArrayList<>());

// then
assertThat(privacy).isEqualTo(Privacy.of("0", "consent", Ccpa.of("1Yn-"), 0));
Expand Down

0 comments on commit 783670e

Please sign in to comment.