Skip to content

Commit

Permalink
Add InvalidAccountConfigException handling for amp, setuid, `co…
Browse files Browse the repository at this point in the history
…okieSync` endpoints.
  • Loading branch information
CTMBNara committed Sep 20, 2023
1 parent cfd03b2 commit b52c398
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ private JsonLogicNode jsonLogicNode(ObjectNode jsonLogicConfig) {

private JsonLogicNode parseJsonLogicNode(String jsonLogicConfig) {
try {
metrics.updateAlertsMetrics(MetricName.general);
return jsonLogic.parse(jsonLogicConfig);
} catch (DecodeException e) {
metrics.updateAlertsMetrics(MetricName.general);
throw new InvalidAccountConfigException("JsonLogic exception: " + e.getMessage());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.prebid.server.cookie.exception.UnauthorizedUidsException;
import org.prebid.server.cookie.model.BiddersContext;
import org.prebid.server.cookie.model.CookieSyncContext;
import org.prebid.server.exception.InvalidAccountConfigException;
import org.prebid.server.execution.Timeout;
import org.prebid.server.execution.TimeoutFactory;
import org.prebid.server.json.DecodeException;
Expand Down Expand Up @@ -223,6 +224,11 @@ private void respondWithError(Throwable error, RoutingContext routingContext) {
body = "Unauthorized: " + message;

metrics.updateUserSyncOptoutMetric();
} else if (error instanceof InvalidAccountConfigException) {
status = HttpResponseStatus.BAD_REQUEST;
body = "Invalid account configuration: " + message;

BAD_REQUEST_LOGGER.info(message, logSamplingRate);
} else {
status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
body = "Unexpected setuid processing error: " + message;
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/prebid/server/handler/SetuidHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.prebid.server.cookie.exception.UnauthorizedUidsException;
import org.prebid.server.cookie.exception.UnavailableForLegalReasonsException;
import org.prebid.server.cookie.model.UidsCookieUpdateResult;
import org.prebid.server.exception.InvalidAccountConfigException;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.execution.Timeout;
import org.prebid.server.execution.TimeoutFactory;
Expand Down Expand Up @@ -342,6 +343,10 @@ private void handleErrors(Throwable error, RoutingContext routingContext, TcfCon
} else if (error instanceof UnavailableForLegalReasonsException) {
status = HttpResponseStatus.valueOf(451);
body = "Unavailable For Legal Reasons.";
} else if (error instanceof InvalidAccountConfigException) {
metrics.updateUserSyncBadRequestMetric();
status = HttpResponseStatus.BAD_REQUEST;
body = "Invalid account configuration: " + message;
} else {
status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
body = "Unexpected setuid processing error: " + message;
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/org/prebid/server/handler/openrtb2/AmpHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.prebid.server.cookie.UidsCookie;
import org.prebid.server.exception.BlacklistedAccountException;
import org.prebid.server.exception.BlacklistedAppException;
import org.prebid.server.exception.InvalidAccountConfigException;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.exception.UnauthorizedAccountException;
Expand Down Expand Up @@ -85,6 +86,7 @@ public class AmpHandler implements Handler<RoutingContext> {
private final HttpInteractionLogger httpInteractionLogger;
private final PrebidVersionProvider prebidVersionProvider;
private final JacksonMapper mapper;
private final double logSamplingRate;

public AmpHandler(AmpRequestFactory ampRequestFactory,
ExchangeService exchangeService,
Expand All @@ -96,7 +98,8 @@ public AmpHandler(AmpRequestFactory ampRequestFactory,
AmpResponsePostProcessor ampResponsePostProcessor,
HttpInteractionLogger httpInteractionLogger,
PrebidVersionProvider prebidVersionProvider,
JacksonMapper mapper) {
JacksonMapper mapper,
double logSamplingRate) {

this.ampRequestFactory = Objects.requireNonNull(ampRequestFactory);
this.exchangeService = Objects.requireNonNull(exchangeService);
Expand All @@ -109,6 +112,7 @@ public AmpHandler(AmpRequestFactory ampRequestFactory,
this.httpInteractionLogger = Objects.requireNonNull(httpInteractionLogger);
this.prebidVersionProvider = Objects.requireNonNull(prebidVersionProvider);
this.mapper = Objects.requireNonNull(mapper);
this.logSamplingRate = logSamplingRate;
}

@Override
Expand Down Expand Up @@ -321,6 +325,14 @@ private void handleResult(AsyncResult<Tuple2<AmpResponse, AuctionContext>> respo
errorMessages = Collections.singletonList(message);
status = HttpResponseStatus.FORBIDDEN;
body = message;
} else if (exception instanceof InvalidAccountConfigException) {
metricRequestStatus = MetricName.bad_requests;
final String message = exception.getMessage();
conditionalLogger.error(message, logSamplingRate);

errorMessages = Collections.singletonList(message);
status = HttpResponseStatus.BAD_REQUEST;
body = "Invalid account configuration: " + message;
} else {
final String message = exception.getMessage();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ AmpHandler openrtbAmpHandler(
ampResponsePostProcessor,
httpInteractionLogger,
prebidVersionProvider,
mapper);
mapper,
logSamplingRate);
}

@Bean
Expand Down
37 changes: 31 additions & 6 deletions src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
import org.prebid.server.cookie.CookieSyncService;
import org.prebid.server.cookie.UidsCookie;
import org.prebid.server.cookie.UidsCookieService;
import org.prebid.server.cookie.exception.CookieSyncException;
import org.prebid.server.cookie.exception.InvalidCookieSyncRequestException;
import org.prebid.server.cookie.exception.UnauthorizedUidsException;
import org.prebid.server.cookie.model.CookieSyncContext;
import org.prebid.server.cookie.model.CookieSyncStatus;
import org.prebid.server.cookie.proto.Uids;
import org.prebid.server.exception.InvalidAccountConfigException;
import org.prebid.server.execution.TimeoutFactory;
import org.prebid.server.metric.Metrics;
import org.prebid.server.privacy.ccpa.Ccpa;
Expand Down Expand Up @@ -181,7 +181,7 @@ public void shouldRespondWithErrorAndSendToAnalyticsWithTcfWhenOptedOut() {
given(uidsCookieService.parseFromRequest(any(RoutingContext.class)))
.willReturn(new UidsCookie(Uids.builder().uids(emptyMap()).optout(true).build(), jacksonMapper));
given(cookieSyncService.processContext(any()))
.willAnswer(answerWithCookieSyncException(
.willAnswer(answerWithException(
tcfContext -> new UnauthorizedUidsException("Sync is not allowed for this uids", tcfContext)));

// when
Expand All @@ -203,7 +203,7 @@ public void shouldRespondWithErrorIfGdprConsentIsMissing() {
// given
given(routingContext.getBody())
.willReturn(givenRequestBody(CookieSyncRequest.builder().bidders(emptySet()).gdpr(1).build()));
given(cookieSyncService.processContext(any())).willAnswer(answerWithCookieSyncException(
given(cookieSyncService.processContext(any())).willAnswer(answerWithException(
tcfContext -> new InvalidCookieSyncRequestException(
"gdpr_consent is required if gdpr is 1", tcfContext)));

Expand Down Expand Up @@ -236,7 +236,7 @@ public void shouldRespondWithBadRequestStatusIfGdprConsentIsInvalid() {
.willReturn(Future.succeededFuture(PrivacyContext.of(null,
TcfContext.builder().inGdprScope(true).consentValid(false).build())));

given(cookieSyncService.processContext(any())).willAnswer(answerWithCookieSyncException(
given(cookieSyncService.processContext(any())).willAnswer(answerWithException(
tcfContext -> new InvalidCookieSyncRequestException("Consent string is invalid", tcfContext)));

// when
Expand All @@ -247,6 +247,31 @@ public void shouldRespondWithBadRequestStatusIfGdprConsentIsInvalid() {
verify(httpResponse).end(eq("Invalid request format: Consent string is invalid"));
}

@Test
public void shouldRespondWithBadRequestStatusOnInvalidAccountConfigException() {
// given
given(routingContext.getBody())
.willReturn(givenRequestBody(CookieSyncRequest.builder()
.bidders(singleton("rubicon"))
.gdpr(1)
.gdprConsent("valid")
.build()));

given(privacyEnforcementService.contextFromCookieSyncRequest(any(), any(), any(), any()))
.willReturn(Future.succeededFuture(PrivacyContext.of(null,
TcfContext.builder().inGdprScope(true).consentValid(true).build())));

given(cookieSyncService.processContext(any())).willAnswer(answerWithException(
tcfContext -> new InvalidAccountConfigException("Message")));

// when
target.handle(routingContext);

// then
verify(httpResponse).setStatusCode(eq(400));
verify(httpResponse).end(eq("Invalid account configuration: Message"));
}

@Test
public void shouldNotSendResponseIfClientClosedConnection() {
// given
Expand Down Expand Up @@ -377,8 +402,8 @@ private static Buffer givenRequestBody(CookieSyncRequest request) {
}
}

private static Answer<Future<? extends Throwable>> answerWithCookieSyncException(
Function<TcfContext, CookieSyncException> exceptionProvider) {
private static Answer<Future<? extends Throwable>> answerWithException(
Function<TcfContext, ? extends Throwable> exceptionProvider) {

return invocation -> {
final TcfContext tcfContext = ((CookieSyncContext) invocation.getArgument(0))
Expand Down
22 changes: 22 additions & 0 deletions src/test/java/org/prebid/server/handler/SetuidHandlerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.prebid.server.cookie.model.UidWithExpiry;
import org.prebid.server.cookie.model.UidsCookieUpdateResult;
import org.prebid.server.cookie.proto.Uids;
import org.prebid.server.exception.InvalidAccountConfigException;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.execution.TimeoutFactory;
import org.prebid.server.metric.Metrics;
Expand Down Expand Up @@ -260,6 +261,27 @@ public void shouldRespondWithUnavailableForLegalReasonsStatusIfDisallowedActivit
verify(httpResponse).end(eq("Unavailable For Legal Reasons."));
}

@Test
public void shouldRespondWithErrorOnInvalidAccountConfigException() {
// given
given(httpRequest.getParam("bidder")).willReturn(RUBICON);
given(httpRequest.getParam("account")).willReturn("accountId");
given(uidsCookieService.parseFromRequest(any(RoutingContext.class)))
.willReturn(emptyUidsCookie());
given(applicationSettings.getAccountById(eq("accountId"), any()))
.willReturn(Future.succeededFuture(Account.builder().build()));

given(gppService.contextFrom(any()))
.willReturn(Future.failedFuture(new InvalidAccountConfigException("Message")));

// when
setuidHandler.handle(routingContext);

// then
verify(httpResponse).setStatusCode(eq(400));
verify(httpResponse).end(eq("Invalid account configuration: Message"));
}

@Test
public void shouldPassUnsuccessfulEventToAnalyticsReporterIfUidMissingInRequest() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.prebid.server.cookie.UidsCookie;
import org.prebid.server.exception.BlacklistedAccountException;
import org.prebid.server.exception.BlacklistedAppException;
import org.prebid.server.exception.InvalidAccountConfigException;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.exception.UnauthorizedAccountException;
import org.prebid.server.execution.Timeout;
Expand Down Expand Up @@ -151,8 +152,8 @@ public void setUp() {
new AmpResponsePostProcessor.NoOpAmpResponsePostProcessor(),
httpInteractionLogger,
prebidVersionProvider,
jacksonMapper
);
jacksonMapper,
0);
}

@Test
Expand Down Expand Up @@ -309,6 +310,28 @@ public void shouldRespondWithUnauthorizedIfAccountIdIsInvalid() {
verify(httpResponse).end(eq("Account id is not provided"));
}

@Test
public void shouldRespondWithBadRequestOnInvalidAccountConfigException() {
// given
given(ampRequestFactory.fromRequest(any(), anyLong()))
.willReturn(Future.failedFuture(new InvalidAccountConfigException("Account is invalid")));

// when
ampHandler.handle(routingContext);

// then
verifyNoInteractions(exchangeService);
verify(httpResponse).setStatusCode(eq(400));

assertThat(httpResponse.headers())
.extracting(Map.Entry::getKey, Map.Entry::getValue)
.containsExactlyInAnyOrder(
tuple("AMP-Access-Control-Allow-Source-Origin", "http://example.com"),
tuple("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin"),
tuple("x-prebid", "pbs-java/1.00"));
verify(httpResponse).end(eq("Invalid account configuration: Account is invalid"));
}

@Test
public void shouldRespondWithInternalServerErrorIfAuctionFails() {
// given
Expand Down

0 comments on commit b52c398

Please sign in to comment.