Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

Commit

Permalink
Core: Add utilization of host cookie value for user.buyeruid (prebid#…
Browse files Browse the repository at this point in the history
  • Loading branch information
And1sS committed Feb 1, 2023
1 parent 3911c37 commit e82f471
Show file tree
Hide file tree
Showing 11 changed files with 542 additions and 108 deletions.
67 changes: 16 additions & 51 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import org.prebid.server.metric.MetricName;
import org.prebid.server.metric.Metrics;
import org.prebid.server.model.CaseInsensitiveMultiMap;
import org.prebid.server.model.UpdateResult;
import org.prebid.server.proto.openrtb.ext.ExtPrebidBidders;
import org.prebid.server.proto.openrtb.ext.request.ExtApp;
import org.prebid.server.proto.openrtb.ext.request.ExtBidderConfigOrtb;
Expand Down Expand Up @@ -167,6 +168,7 @@ public class ExchangeService {
private final SupplyChainResolver supplyChainResolver;
private final DebugResolver debugResolver;
private final MediaTypeProcessor mediaTypeProcessor;
private final UidUpdater uidUpdater;
private final TimeoutResolver timeoutResolver;
private final TimeoutFactory timeoutFactory;
private final BidRequestOrtbVersionConversionManager ortbVersionConversionManager;
Expand Down Expand Up @@ -195,6 +197,7 @@ public ExchangeService(int timeoutAdjustmentFactor,
SupplyChainResolver supplyChainResolver,
DebugResolver debugResolver,
MediaTypeProcessor mediaTypeProcessor,
UidUpdater uidUpdater,
TimeoutResolver timeoutResolver,
TimeoutFactory timeoutFactory,
BidRequestOrtbVersionConversionManager ortbVersionConversionManager,
Expand Down Expand Up @@ -226,6 +229,7 @@ public ExchangeService(int timeoutAdjustmentFactor,
this.supplyChainResolver = Objects.requireNonNull(supplyChainResolver);
this.debugResolver = Objects.requireNonNull(debugResolver);
this.mediaTypeProcessor = Objects.requireNonNull(mediaTypeProcessor);
this.uidUpdater = Objects.requireNonNull(uidUpdater);
this.timeoutResolver = Objects.requireNonNull(timeoutResolver);
this.timeoutFactory = Objects.requireNonNull(timeoutFactory);
this.ortbVersionConversionManager = Objects.requireNonNull(ortbVersionConversionManager);
Expand Down Expand Up @@ -547,17 +551,13 @@ private Future<List<AuctionParticipation>> makeAuctionParticipation(
Map<String, MultiBidConfig> bidderToMultiBid) {

final BidRequest bidRequest = context.getBidRequest();
final User user = bidRequest.getUser();
final ExtUser extUser = user != null ? user.getExt() : null;
final Map<String, String> uidsBody = uidsFromBody(extUser);

final ExtRequest requestExt = bidRequest.getExt();
final ExtRequestPrebid prebid = requestExt == null ? null : requestExt.getPrebid();
final Map<String, ExtBidderConfigOrtb> biddersToConfigs = getBiddersToConfigs(prebid);
final Map<String, List<String>> eidPermissions = getEidPermissions(prebid);
final Map<String, User> bidderToUser =
prepareUsers(bidders, context, aliases, bidRequest, extUser, uidsBody, biddersToConfigs,
eidPermissions);
prepareUsers(bidders, context, aliases, biddersToConfigs, eidPermissions);

return privacyEnforcementService
.mask(context, bidderToUser, bidders, aliases)
Expand Down Expand Up @@ -626,12 +626,10 @@ private static List<String> firstPartyDataBidders(ExtRequest requestExt) {
private Map<String, User> prepareUsers(List<String> bidders,
AuctionContext context,
BidderAliases aliases,
BidRequest bidRequest,
ExtUser extUser,
Map<String, String> uidsBody,
Map<String, ExtBidderConfigOrtb> biddersToConfigs,
Map<String, List<String>> eidPermissions) {

final BidRequest bidRequest = context.getBidRequest();
final List<String> firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt());

final Map<String, User> bidderToUser = new HashMap<>();
Expand All @@ -640,8 +638,8 @@ private Map<String, User> prepareUsers(List<String> bidders,
biddersToConfigs.get(ALL_BIDDERS_CONFIG));

final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.contains(bidder);
final User preparedUser = prepareUser(bidRequest.getUser(), extUser, bidder, aliases, uidsBody,
context.getUidsCookie(), useFirstPartyData, fpdConfig, eidPermissions);
final User preparedUser = prepareUser(
bidder, context, aliases, useFirstPartyData, fpdConfig, eidPermissions);
bidderToUser.put(bidder, preparedUser);
}
return bidderToUser;
Expand All @@ -654,17 +652,17 @@ private Map<String, User> prepareUsers(List<String> bidders,
* Also, removes user.ext.prebid (if present), user.ext.data and user.data (in case bidder does not use first
* party data).
*/
private User prepareUser(User user,
ExtUser extUser,
String bidder,
private User prepareUser(String bidder,
AuctionContext context,
BidderAliases aliases,
Map<String, String> uidsBody,
UidsCookie uidsCookie,
boolean useFirstPartyData,
ExtBidderConfigOrtb fpdConfig,
Map<String, List<String>> eidPermissions) {

final String updatedBuyerUid = updateUserBuyerUid(user, bidder, aliases, uidsBody, uidsCookie);
final User user = context.getBidRequest().getUser();
final ExtUser extUser = user != null ? user.getExt() : null;

final UpdateResult<String> buyerUidUpdateResult = uidUpdater.updateUid(bidder, context, aliases);
final List<Eid> userEids = extractUserEids(user);
final List<Eid> allowedUserEids = resolveAllowedEids(userEids, bidder, eidPermissions);
final boolean shouldUpdateUserEids = allowedUserEids.size() != CollectionUtils.emptyIfNull(userEids).size();
Expand All @@ -674,11 +672,9 @@ private User prepareUser(User user,
final boolean shouldCleanData = user != null && user.getData() != null && !useFirstPartyData;

User maskedUser = user;
if (updatedBuyerUid != null || shouldUpdateUserEids || shouldUpdateUserExt || shouldCleanData) {
if (buyerUidUpdateResult.isUpdated() || shouldUpdateUserEids || shouldUpdateUserExt || shouldCleanData) {
final User.UserBuilder userBuilder = user == null ? User.builder() : user.toBuilder();
if (updatedBuyerUid != null) {
userBuilder.buyeruid(updatedBuyerUid);
}
userBuilder.buyeruid(buyerUidUpdateResult.getValue());

if (shouldUpdateUserEids) {
userBuilder.eids(nullIfEmpty(allowedUserEids));
Expand All @@ -704,19 +700,6 @@ private User prepareUser(User user,
: maskedUser;
}

/**
* Returns updated buyerUid or null if it doesn't need to be updated.
*/
private String updateUserBuyerUid(User user, String bidder, BidderAliases aliases,
Map<String, String> uidsBody, UidsCookie uidsCookie) {
final String buyerUidFromBodyOrCookie = extractUid(uidsBody, uidsCookie, aliases.resolveBidder(bidder));
final String buyerUidFromUser = user != null ? user.getBuyeruid() : null;

return StringUtils.isBlank(buyerUidFromUser) && StringUtils.isNotBlank(buyerUidFromBodyOrCookie)
? buyerUidFromBodyOrCookie
: null;
}

private List<Eid> extractUserEids(User user) {
return user != null ? user.getEids() : null;
}
Expand All @@ -743,24 +726,6 @@ private boolean isUserEidAllowed(String source, Map<String, List<String>> eidPer
|| allowedBidders.contains(bidder);
}

/**
* Extracts UID from uids from body or {@link UidsCookie}.
*/
private String extractUid(Map<String, String> uidsBody, UidsCookie uidsCookie, String bidder) {
final String uid = uidsBody.get(bidder);
return StringUtils.isNotBlank(uid) ? uid : uidsCookie.uidFrom(resolveCookieFamilyName(bidder));
}

/**
* Extract cookie family name from bidder's {@link Usersyncer} if it is enabled. If not - return null.
*/
private String resolveCookieFamilyName(String bidder) {
return bidderCatalog.usersyncerByName(bidder)
.filter(usersyncer -> bidderCatalog.isActive(bidder))
.map(Usersyncer::getCookieFamilyName)
.orElse(null);
}

/**
* Returns shuffled list of {@link AuctionParticipation} with {@link BidRequest}.
*/
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/org/prebid/server/auction/UidUpdater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.prebid.server.auction;

import com.iab.openrtb.request.User;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.auction.model.AuctionContext;
import org.prebid.server.bidder.BidderCatalog;
import org.prebid.server.cookie.UidsCookie;
import org.prebid.server.cookie.UidsCookieService;
import org.prebid.server.model.UpdateResult;
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
import org.prebid.server.proto.openrtb.ext.request.ExtUserPrebid;

import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

public class UidUpdater {

private final String hostCookieFamily;
private final BidderCatalog bidderCatalog;
private final UidsCookieService uidsCookieService;

public UidUpdater(String hostCookieFamily, BidderCatalog bidderCatalog, UidsCookieService uidsCookieService) {
this.hostCookieFamily = hostCookieFamily;
this.bidderCatalog = Objects.requireNonNull(bidderCatalog);
this.uidsCookieService = Objects.requireNonNull(uidsCookieService);
}

public UpdateResult<String> updateUid(String bidder, AuctionContext auctionContext, BidderAliases aliases) {
final User user = auctionContext.getBidRequest().getUser();

final String uidFromUser = user != null ? user.getBuyeruid() : null;
if (StringUtils.isNotBlank(uidFromUser)) {
return UpdateResult.unaltered(uidFromUser);
}

final String resolvedBidder = aliases.resolveBidder(bidder);

final String uidFromExt = uidFromExtUser(user, resolvedBidder);
final String uidFromUidsCookie = uidFromUidsCookie(auctionContext.getUidsCookie(), resolvedBidder);
final String uidFromHostCookie = uidFromHostCookie(auctionContext, resolvedBidder);

return Stream.of(uidFromExt, uidFromUidsCookie, uidFromHostCookie)
.filter(StringUtils::isNotBlank)
.map(UpdateResult::updated)
.findFirst()
.orElse(UpdateResult.unaltered(null));
}

private static String uidFromExtUser(User user, String bidder) {
return Optional.ofNullable(user)
.map(User::getExt)
.map(ExtUser::getPrebid)
.map(ExtUserPrebid::getBuyeruids)
.map(uids -> uids.get(bidder))
.orElse(null);
}

private String uidFromUidsCookie(UidsCookie uidsCookie, String bidder) {
return bidderCatalog.cookieFamilyName(bidder)
.map(uidsCookie::uidFrom)
.orElse(null);
}

private String uidFromHostCookie(AuctionContext auctionContext, String bidder) {
return bidderCatalog.cookieFamilyName(bidder)
.filter(cookieFamily -> StringUtils.equals(cookieFamily, hostCookieFamily))
.map(cookieFamily -> uidsCookieService.parseHostCookie(auctionContext.getHttpRequest()))
.orElse(null);
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/prebid/server/cookie/UidsCookieService.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ public String parseHostCookie(Map<String, String> cookies) {
return hostCookieName != null ? cookies.get(hostCookieName) : null;
}

/**
* Lookups host cookie value from request context by configured host cookie name.
*/
public String parseHostCookie(HttpRequestContext httpRequest) {
return parseHostCookie(HttpUtil.cookiesAsMap(httpRequest));
}

/**
* Checks incoming request if it matches pre-configured opted-out cookie name, value and de-activates
* UIDs cookie sync.
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/org/prebid/server/model/UpdateResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.prebid.server.model;

import lombok.Value;

@Value
public class UpdateResult<T> {

boolean updated;

T value;

public static <T> UpdateResult<T> unaltered(T value) {
return new UpdateResult<>(false, value);
}

public static <T> UpdateResult<T> updated(T value) {
return new UpdateResult<>(true, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.prebid.server.auction.StoredResponseProcessor;
import org.prebid.server.auction.SupplyChainResolver;
import org.prebid.server.auction.TimeoutResolver;
import org.prebid.server.auction.UidUpdater;
import org.prebid.server.auction.VideoResponseFactory;
import org.prebid.server.auction.VideoStoredRequestProcessor;
import org.prebid.server.auction.WinningBidComparatorFactory;
Expand Down Expand Up @@ -581,6 +582,15 @@ UidsCookieService uidsCookieService(
mapper);
}

@Bean
UidUpdater uidUpdater(
@Value("${host-cookie.family:#{null}}") String hostCookieFamily,
BidderCatalog bidderCatalog,
UidsCookieService uidsCookieService) {

return new UidUpdater(hostCookieFamily, bidderCatalog, uidsCookieService);
}

@Bean
CoopSyncProvider coopSyncProvider(
BidderCatalog bidderCatalog,
Expand Down Expand Up @@ -726,6 +736,7 @@ ExchangeService exchangeService(
SupplyChainResolver supplyChainResolver,
DebugResolver debugResolver,
MediaTypeProcessor mediaTypeProcessor,
UidUpdater uidUpdater,
TimeoutResolver timeoutResolver,
TimeoutFactory timeoutFactory,
BidRequestOrtbVersionConversionManager bidRequestOrtbVersionConversionManager,
Expand Down Expand Up @@ -755,6 +766,7 @@ ExchangeService exchangeService(
supplyChainResolver,
debugResolver,
mediaTypeProcessor,
uidUpdater,
timeoutResolver,
timeoutFactory,
bidRequestOrtbVersionConversionManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ class UserExt {
List<String> fcapids
UserTime time
UserExtData data
UserExtPrebid prebid
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.prebid.server.functional.model.request.auction

import groovy.transform.ToString
import org.prebid.server.functional.model.bidder.BidderName

@ToString(includeNames = true, ignoreNulls = true)
class UserExtPrebid {

Map<BidderName, String> buyeruids
}
Loading

0 comments on commit e82f471

Please sign in to comment.