diff --git a/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java b/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java index a36b3778398..4b486d3837c 100644 --- a/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java +++ b/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java @@ -10,8 +10,10 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; +import org.prebid.server.auction.model.IpAddress; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.execution.Timeout; import org.prebid.server.metric.MetricName; @@ -32,7 +34,6 @@ import org.prebid.server.proto.request.CookieSyncRequest; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountGdprConfig; -import org.prebid.server.util.HttpUtil; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -62,6 +63,7 @@ public class PrivacyEnforcementService { private final BidderCatalog bidderCatalog; private final PrivacyExtractor privacyExtractor; private final TcfDefinerService tcfDefinerService; + private final ImplicitParametersExtractor implicitParametersExtractor; private final IpAddressHelper ipAddressHelper; private final Metrics metrics; private final boolean ccpaEnforce; @@ -70,6 +72,7 @@ public class PrivacyEnforcementService { public PrivacyEnforcementService(BidderCatalog bidderCatalog, PrivacyExtractor privacyExtractor, TcfDefinerService tcfDefinerService, + ImplicitParametersExtractor implicitParametersExtractor, IpAddressHelper ipAddressHelper, Metrics metrics, boolean ccpaEnforce, @@ -78,6 +81,7 @@ public PrivacyEnforcementService(BidderCatalog bidderCatalog, this.bidderCatalog = Objects.requireNonNull(bidderCatalog); this.privacyExtractor = Objects.requireNonNull(privacyExtractor); this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); + this.implicitParametersExtractor = Objects.requireNonNull(implicitParametersExtractor); this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); this.metrics = Objects.requireNonNull(metrics); this.ccpaEnforce = ccpaEnforce; @@ -94,14 +98,11 @@ public Future contextFromBidRequest(AuctionContext auctionContex final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest, errors); final Device device = bidRequest.getDevice(); - final String ipAddress = device != null ? device.getIp() : null; final Geo geo = device != null ? device.getGeo() : null; final String country = geo != null ? geo.getCountry() : null; - final String effectiveIpAddress = isCoppaMaskingRequired(privacy) || isLmtEnabled(device) - ? ipAddressHelper.maskIpv4(ipAddress) - : ipAddress; + final String effectiveIpAddress = resolveIpAddress(device, privacy); final AccountGdprConfig accountGdpr = account.getGdpr(); final String accountId = account.getId(); @@ -112,11 +113,27 @@ public Future contextFromBidRequest(AuctionContext auctionContex .map(tcfContext -> PrivacyContext.of(privacy, tcfContext, tcfContext.getIpAddress())); } + private String resolveIpAddress(Device device, Privacy privacy) { + final boolean shouldBeMasked = isCoppaMaskingRequired(privacy) || isLmtEnabled(device); + + final String ipV4Address = device != null ? device.getIp() : null; + if (StringUtils.isNotBlank(ipV4Address)) { + return shouldBeMasked ? ipAddressHelper.maskIpv4(ipV4Address) : ipV4Address; + } + + final String ipV6Address = device != null ? device.getIpv6() : null; + if (StringUtils.isNotBlank(ipV6Address)) { + return shouldBeMasked ? ipAddressHelper.anonymizeIpv6(ipV6Address) : ipV6Address; + } + + return null; + } + public Future contextFromSetuidRequest( HttpServerRequest httpRequest, Account account, Timeout timeout) { final Privacy privacy = privacyExtractor.validPrivacyFromSetuidRequest(httpRequest); - final String ipAddress = HttpUtil.ipFrom(httpRequest); + final String ipAddress = resolveIpFromRequest(httpRequest); final AccountGdprConfig accountGdpr = account.getGdpr(); final String accountId = account.getId(); final RequestLogInfo requestLogInfo = requestLogInfo(MetricName.setuid, null, accountId); @@ -130,7 +147,7 @@ public Future contextFromCookieSyncRequest( CookieSyncRequest cookieSyncRequest, HttpServerRequest httpRequest, Account account, Timeout timeout) { final Privacy privacy = privacyExtractor.validPrivacyFrom(cookieSyncRequest); - final String ipAddress = HttpUtil.ipFrom(httpRequest); + final String ipAddress = resolveIpFromRequest(httpRequest); final AccountGdprConfig accountGdpr = account.getGdpr(); final String accountId = account.getId(); final RequestLogInfo requestLogInfo = requestLogInfo(MetricName.cookiesync, null, accountId); @@ -140,6 +157,16 @@ public Future contextFromCookieSyncRequest( .map(tcfContext -> PrivacyContext.of(privacy, tcfContext)); } + private String resolveIpFromRequest(HttpServerRequest request) { + final List requestIps = implicitParametersExtractor.ipFrom(request); + return requestIps.stream() + .map(ipAddressHelper::toIpAddress) + .filter(Objects::nonNull) + .map(IpAddress::getIp) + .findFirst() + .orElse(null); + } + private static RequestLogInfo requestLogInfo(MetricName requestType, BidRequest bidRequest, String accountId) { if (Objects.equals(requestType, MetricName.openrtb2web)) { final Site site = bidRequest != null ? bidRequest.getSite() : null; diff --git a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java index da503c6786f..56c78ebc2c2 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -8,6 +8,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.IpAddress; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.execution.Timeout; import org.prebid.server.geolocation.GeoLocationService; @@ -83,6 +84,9 @@ public TcfDefinerService(GdprConfig gdprConfig, this.metrics = Objects.requireNonNull(metrics); } + /** + * Used for auctions. + */ public Future resolveTcfContext(Privacy privacy, String country, String ipAddress, @@ -99,6 +103,9 @@ public Future resolveTcfContext(Privacy privacy, .map(this::updateTcfGeoMetrics); } + /** + * Used for cookie sync and setuid. + */ public Future resolveTcfContext(Privacy privacy, String ipAddress, AccountGdprConfig accountGdprConfig, @@ -234,7 +241,18 @@ private Future toTcfContext(Privacy privacy, } private String maybeMaskIp(String ipAddress, TCString consent) { - return shouldMaskIp(consent) ? ipAddressHelper.maskIpv4(ipAddress) : ipAddress; + if (!shouldMaskIp(consent)) { + return ipAddress; + } + + final IpAddress ip = ipAddressHelper.toIpAddress(ipAddress); + if (ip == null) { + return ipAddress; + } + + return ip.getVersion() == IpAddress.IP.v4 + ? ipAddressHelper.maskIpv4(ipAddress) + : ipAddressHelper.anonymizeIpv6(ipAddress); } private static boolean shouldMaskIp(TCString consent) { diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index d334fdb27c3..26a06ec67d3 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -585,13 +585,21 @@ PrivacyEnforcementService privacyEnforcementService( BidderCatalog bidderCatalog, PrivacyExtractor privacyExtractor, TcfDefinerService tcfDefinerService, + ImplicitParametersExtractor implicitParametersExtractor, IpAddressHelper ipAddressHelper, Metrics metrics, @Value("${ccpa.enforce}") boolean ccpaEnforce, @Value("${lmt.enforce}") boolean lmtEnforce) { return new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, ccpaEnforce, lmtEnforce); + bidderCatalog, + privacyExtractor, + tcfDefinerService, + implicitParametersExtractor, + ipAddressHelper, + metrics, + ccpaEnforce, + lmtEnforce); } @Bean diff --git a/src/main/java/org/prebid/server/util/HttpUtil.java b/src/main/java/org/prebid/server/util/HttpUtil.java index 3344f8d0e04..9a961025f89 100644 --- a/src/main/java/org/prebid/server/util/HttpUtil.java +++ b/src/main/java/org/prebid/server/util/HttpUtil.java @@ -5,7 +5,6 @@ import io.vertx.core.MultiMap; import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpHeaders; -import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; import org.apache.commons.lang3.StringUtils; @@ -109,23 +108,6 @@ public static void addHeaderIfValueIsNotEmpty(MultiMap headers, CharSequence hea } } - /** - * Determines IP-Address by checking "X-Forwarded-For", "X-Real-IP" http headers or remote host address - * if both are empty. - */ - public static String ipFrom(HttpServerRequest request) { - // X-Forwarded-For: client1, proxy1, proxy2 - String ip = StringUtils.trimToNull( - StringUtils.substringBefore(request.headers().get("X-Forwarded-For"), ",")); - if (ip == null) { - ip = StringUtils.trimToNull(request.headers().get("X-Real-IP")); - } - if (ip == null) { - ip = StringUtils.trimToNull(request.remoteAddress().host()); - } - return ip; - } - public static String getDomainFromUrl(String url) { if (StringUtils.isBlank(url)) { return null; @@ -147,7 +129,7 @@ public static String toSetCookieHeaderValue(Cookie cookie) { } /** - * Sends HTTP response according to the given status and body + * Sends HTTP response according to the given status and body. */ public static void respondWith(RoutingContext context, HttpResponseStatus status, String body) { final HttpServerResponse response = context.response().setStatusCode(status.code()); diff --git a/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java b/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java index 01300fd3da7..69ee8a23214 100644 --- a/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java +++ b/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java @@ -8,7 +8,6 @@ import com.iab.openrtb.request.Site; import com.iab.openrtb.request.User; import io.vertx.core.Future; -import io.vertx.core.MultiMap; import io.vertx.core.http.HttpServerRequest; import org.junit.Before; import org.junit.Rule; @@ -20,6 +19,7 @@ import org.prebid.server.assertion.FutureAssertion; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; +import org.prebid.server.auction.model.IpAddress; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.execution.Timeout; @@ -94,6 +94,8 @@ public class PrivacyEnforcementServiceTest extends VertxTest { @Mock private TcfDefinerService tcfDefinerService; @Mock + private ImplicitParametersExtractor implicitParametersExtractor; + @Mock private IpAddressHelper ipAddressHelper; @Mock private Metrics metrics; @@ -119,7 +121,8 @@ public void setUp() { privacyExtractor = new PrivacyExtractor(); privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, false, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, false, false); } @Test @@ -242,13 +245,63 @@ public void contextFromBidRequestShouldReturnTcfContextWithIpMasked() { eq(privacy), isNull(), eq("ip-masked"), isNull(), same(MetricName.openrtb2web), any(), isNull()); } + @Test + public void contextFromBidRequestShouldCallResolveTcfContextWithIpv6AnonymizedWhenIpNotPresentAndLmtIsOne() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder() + .lmt(1) + .ipv6("ipv6") + .build()) + .build(); + given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip-masked"); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.builder().build())); + final AuctionContext auctionContext = AuctionContext.builder() + .bidRequest(bidRequest) + .account(Account.empty("account")) + .prebidErrors(new ArrayList<>()) + .build(); + + // when + privacyEnforcementService.contextFromBidRequest(auctionContext); + + // then + verify(tcfDefinerService).resolveTcfContext(any(), any(), eq("ip-masked"), any(), any(), any(), any()); + } + + @Test + public void contextFromBidRequestShouldCallResolveTcfContextWithIpv6WhenIpv4NotPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder() + .ipv6("ipv6") + .build()) + .build(); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.builder().build())); + final AuctionContext auctionContext = AuctionContext.builder() + .bidRequest(bidRequest) + .account(Account.empty("account")) + .prebidErrors(new ArrayList<>()) + .build(); + + // when + privacyEnforcementService.contextFromBidRequest(auctionContext); + + // then + verify(tcfDefinerService).resolveTcfContext(any(), any(), eq("ipv6"), any(), any(), any(), any()); + } + @Test public void contextFromSetuidRequestShouldReturnContext() { // given - final HttpServerRequest request = mock(HttpServerRequest.class); - given(request.getParam("gdpr")).willReturn("1"); - given(request.getParam("gdpr_consent")).willReturn("consent"); - given(request.headers()).willReturn(MultiMap.caseInsensitiveMultiMap().add("X-Forwarded-For", "ip")); + final HttpServerRequest httpRequest = mock(HttpServerRequest.class); + given(httpRequest.getParam("gdpr")).willReturn("1"); + given(httpRequest.getParam("gdpr_consent")).willReturn("consent"); + + given(implicitParametersExtractor.ipFrom(httpRequest)).willReturn(singletonList("ip")); + given(ipAddressHelper.toIpAddress(anyString())).willReturn(IpAddress.of("ip", IpAddress.IP.v4)); final TcfContext tcfContext = TcfContext.builder() .gdpr("1") @@ -262,7 +315,7 @@ public void contextFromSetuidRequestShouldReturnContext() { // when final Future privacyContext = privacyEnforcementService.contextFromSetuidRequest( - request, Account.empty(accountId), null); + httpRequest, Account.empty(accountId), null); // then final Privacy privacy = Privacy.of("1", "consent", Ccpa.EMPTY, 0); @@ -276,9 +329,9 @@ public void contextFromSetuidRequestShouldReturnContext() { @Test public void contextFromCookieSyncRequestShouldReturnContext() { // given - final HttpServerRequest httpServerRequest = mock(HttpServerRequest.class); - given(httpServerRequest.headers()) - .willReturn(MultiMap.caseInsensitiveMultiMap().add("X-Forwarded-For", "ip")); + final HttpServerRequest httpRequest = mock(HttpServerRequest.class); + given(implicitParametersExtractor.ipFrom(httpRequest)).willReturn(singletonList("ip")); + given(ipAddressHelper.toIpAddress(anyString())).willReturn(IpAddress.of("ip", IpAddress.IP.v4)); final CookieSyncRequest cookieSyncRequest = CookieSyncRequest.builder() .gdpr(1) @@ -298,7 +351,7 @@ public void contextFromCookieSyncRequestShouldReturnContext() { // when final Future privacyContext = privacyEnforcementService.contextFromCookieSyncRequest( - cookieSyncRequest, httpServerRequest, Account.empty(accountId), null); + cookieSyncRequest, httpRequest, Account.empty(accountId), null); // then final Privacy privacy = Privacy.of("1", "consent", Ccpa.of("1YYY"), 0); @@ -345,7 +398,8 @@ public void shouldMaskForCoppaWhenDeviceLmtIsEnforceAndOneAndRegsCoppaIsOneAndDo public void shouldMaskForCcpaWhenUsPolicyIsValidAndCoppaIsZero() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, true, false); given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfResponse.of(true, emptyMap(), null))); @@ -482,7 +536,8 @@ public void shouldNotMaskWhenDeviceLmtIsZeroAndCoppaIsZeroAndGdprIsZeroAndTcfDef public void shouldMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOneAndLmtIsEnforced() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, false, true); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, false, true); given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) .willReturn(Future.succeededFuture( @@ -1112,7 +1167,8 @@ public void shouldReturnFailedFutureWhenTcfServiceIsReturnFailedFuture() { public void shouldMaskForCcpaAndTcfWhenUsPolicyIsValidAndGdprIsEnforcedAndCOPPAIsZero() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, true, false); final String bidder1Name = "bidder1Name"; final String bidder2Name = "bidder2Name"; @@ -1182,7 +1238,8 @@ public void shouldMaskForCcpaAndTcfWhenUsPolicyIsValidAndGdprIsEnforcedAndCOPPAI public void shouldNotMaskForCcpaWhenCatchAllWildcardIsPresentInNosaleList() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, true, false); given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) .willReturn(Future.succeededFuture( @@ -1236,7 +1293,8 @@ public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsFalseInConfigur public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrueInConfigurationAndFalseInAccount() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, true, false); final Ccpa ccpa = Ccpa.of("1YYY"); final Account account = Account.builder().enforceCcpa(false).build(); @@ -1249,7 +1307,8 @@ public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrueInConfigura public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrue() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, true, false); final Ccpa ccpa = Ccpa.of("1YNY"); final Account account = Account.builder().build(); @@ -1262,7 +1321,8 @@ public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrue() { public void isCcpaEnforcedShouldReturnTrueWhenEnforcedPropertyIsTrueAndCcpaReturnsTrue() { // given privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false); + bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, + metrics, true, false); final Ccpa ccpa = Ccpa.of("1YYY"); final Account account = Account.builder().build(); diff --git a/src/test/java/org/prebid/server/auction/requestfactory/Ortb2ImplicitParametersResolverTest.java b/src/test/java/org/prebid/server/auction/requestfactory/Ortb2ImplicitParametersResolverTest.java index 479dfa3ae26..6c4c1b52667 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/Ortb2ImplicitParametersResolverTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/Ortb2ImplicitParametersResolverTest.java @@ -58,6 +58,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; public class Ortb2ImplicitParametersResolverTest extends VertxTest { @@ -229,6 +230,23 @@ public void shouldSetAnonymizedIpv6FromField() { .isEqualTo(Device.builder().ipv6("2001:0db8:85a3:0000::").ua("UnitTest").build()); } + @Test + public void shouldNotImplicitlyResolveIpIfIpv6IsPassed() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder().ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334").build()) + .build(); + + given(ipAddressHelper.toIpAddress(eq("2001:0db8:85a3:0000:0000:8a2e:0370:7334"))) + .willReturn(IpAddress.of("2001:0db8:85a3:0000::", IpAddress.IP.v6)); + + // when + target.resolve(bidRequest, routingContext, timeoutResolver); + + // then + verify(paramsExtractor, never()).ipFrom(any()); + } + @Test public void shouldNotSetDeviceDntIfHeaderHasInvalidValue() { // given diff --git a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java index dfaa799f7ee..f8c1d7aab64 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java @@ -9,6 +9,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.IpAddress; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.geolocation.GeoLocationService; import org.prebid.server.geolocation.model.GeoInfo; @@ -268,6 +269,7 @@ public void resolveTcfContextShouldReturnGdprFromCountryWhenGdprFromRequestIsNot @Test public void resolveTcfContextShouldReturnGdprFromGeoLocationServiceWhenGdprFromRequestIsNotValid() { // given + given(ipAddressHelper.toIpAddress(anyString())).willReturn(IpAddress.of("ip", IpAddress.IP.v4)); given(ipAddressHelper.maskIpv4(anyString())).willReturn("ip-masked"); final GeoInfo geoInfo = GeoInfo.builder().vendor("vendor").country("ua").build();