Use a phased enrollment strategy for the `pnp` compatibility flag

This commit is contained in:
Jon Chambers 2024-02-18 17:21:11 -05:00 committed by Jon Chambers
parent 11e6ff1bbe
commit 3e12a8780d
7 changed files with 31 additions and 21 deletions

View File

@ -901,6 +901,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
dynamicConfigurationManager, zkSecretParams, spamChecker), dynamicConfigurationManager, zkSecretParams, spamChecker),
new PaymentsController(currencyManager, paymentsCredentialsGenerator), new PaymentsController(currencyManager, paymentsCredentialsGenerator),
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager, new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager,
experimentEnrollmentManager,
profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner,
config.getCdnConfiguration().bucket(), zkProfileOperations, batchIdentityCheckExecutor), config.getCdnConfiguration().bucket(), zkProfileOperations, batchIdentityCheckExecutor),
new ProvisioningController(rateLimiters, provisioningManager), new ProvisioningController(rateLimiters, provisioningManager),

View File

@ -81,6 +81,7 @@ import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialPro
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes; import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
import org.whispersystems.textsecuregcm.entities.UserCapabilities; import org.whispersystems.textsecuregcm.entities.UserCapabilities;
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse; import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier; import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier; import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
@ -112,6 +113,7 @@ public class ProfileController {
private final ProfilesManager profilesManager; private final ProfilesManager profilesManager;
private final AccountsManager accountsManager; private final AccountsManager accountsManager;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager; private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
private final ExperimentEnrollmentManager experimentEnrollmentManager;
private final ProfileBadgeConverter profileBadgeConverter; private final ProfileBadgeConverter profileBadgeConverter;
private final Map<String, BadgeConfiguration> badgeConfigurationMap; private final Map<String, BadgeConfiguration> badgeConfigurationMap;
@ -129,26 +131,29 @@ public class ProfileController {
private static final Counter VERSION_NOT_FOUND_COUNTER = Metrics.counter(name(ProfileController.class, "versionNotFound")); private static final Counter VERSION_NOT_FOUND_COUNTER = Metrics.counter(name(ProfileController.class, "versionNotFound"));
private static final String INVALID_ACCEPT_LANGUAGE_COUNTER_NAME = name(ProfileController.class, "invalidAcceptLanguage"); private static final String INVALID_ACCEPT_LANGUAGE_COUNTER_NAME = name(ProfileController.class, "invalidAcceptLanguage");
private static final String PNP_FLAG_EXPERIMENT_NAME = "pnpCompatibilityFlag";
public ProfileController( public ProfileController(
Clock clock, Clock clock,
RateLimiters rateLimiters, RateLimiters rateLimiters,
AccountsManager accountsManager, AccountsManager accountsManager,
ProfilesManager profilesManager, ProfilesManager profilesManager,
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager, DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager, ExperimentEnrollmentManager experimentEnrollmentManager,
ProfileBadgeConverter profileBadgeConverter, ProfileBadgeConverter profileBadgeConverter,
BadgesConfiguration badgesConfiguration, BadgesConfiguration badgesConfiguration,
S3Client s3client, S3Client s3client,
PostPolicyGenerator policyGenerator, PostPolicyGenerator policyGenerator,
PolicySigner policySigner, PolicySigner policySigner,
String bucket, String bucket,
ServerZkProfileOperations zkProfileOperations, ServerZkProfileOperations zkProfileOperations,
Executor batchIdentityCheckExecutor) { Executor batchIdentityCheckExecutor) {
this.clock = clock; this.clock = clock;
this.rateLimiters = rateLimiters; this.rateLimiters = rateLimiters;
this.accountsManager = accountsManager; this.accountsManager = accountsManager;
this.profilesManager = profilesManager; this.profilesManager = profilesManager;
this.dynamicConfigurationManager = dynamicConfigurationManager; this.dynamicConfigurationManager = dynamicConfigurationManager;
this.profileBadgeConverter = profileBadgeConverter; this.experimentEnrollmentManager = experimentEnrollmentManager;
this.profileBadgeConverter = profileBadgeConverter;
this.badgeConfigurationMap = badgesConfiguration.getBadges().stream().collect(Collectors.toMap( this.badgeConfigurationMap = badgesConfiguration.getBadges().stream().collect(Collectors.toMap(
BadgeConfiguration::getId, Function.identity())); BadgeConfiguration::getId, Function.identity()));
this.zkProfileOperations = zkProfileOperations; this.zkProfileOperations = zkProfileOperations;
@ -437,7 +442,8 @@ public class ProfileController {
return new BaseProfileResponse(account.getIdentityKey(IdentityType.ACI), return new BaseProfileResponse(account.getIdentityKey(IdentityType.ACI),
account.getUnidentifiedAccessKey().map(UnidentifiedAccessChecksum::generateFor).orElse(null), account.getUnidentifiedAccessKey().map(UnidentifiedAccessChecksum::generateFor).orElse(null),
account.isUnrestrictedUnidentifiedAccess(), account.isUnrestrictedUnidentifiedAccess(),
UserCapabilities.createForAccount(account), UserCapabilities.createForAccount(account,
experimentEnrollmentManager.isEnrolled(account.getIdentifier(IdentityType.ACI), PNP_FLAG_EXPERIMENT_NAME)),
profileBadgeConverter.convert( profileBadgeConverter.convert(
getAcceptableLanguagesForRequest(containerRequestContext), getAcceptableLanguagesForRequest(containerRequestContext),
account.getBadges(), account.getBadges(),
@ -449,7 +455,8 @@ public class ProfileController {
return new BaseProfileResponse(account.getIdentityKey(IdentityType.PNI), return new BaseProfileResponse(account.getIdentityKey(IdentityType.PNI),
null, null,
false, false,
UserCapabilities.createForAccount(account), UserCapabilities.createForAccount(account,
experimentEnrollmentManager.isEnrolled(account.getIdentifier(IdentityType.ACI), PNP_FLAG_EXPERIMENT_NAME)),
Collections.emptyList(), Collections.emptyList(),
new PniServiceIdentifier(account.getPhoneNumberIdentifier())); new PniServiceIdentifier(account.getPhoneNumberIdentifier()));
} }

View File

@ -12,7 +12,7 @@ public record UserCapabilities(boolean paymentActivation,
boolean pni, boolean pni,
boolean pnp) { boolean pnp) {
public static UserCapabilities createForAccount(final Account account) { public static UserCapabilities createForAccount(final Account account, final boolean includePnpFlag) {
return new UserCapabilities(account.isPaymentActivationSupported(), true, true); return new UserCapabilities(account.isPaymentActivationSupported(), true, includePnpFlag);
} }
} }

View File

@ -105,7 +105,7 @@ public class ProfileGrpcHelper {
final ProfileBadgeConverter profileBadgeConverter) { final ProfileBadgeConverter profileBadgeConverter) {
final GetUnversionedProfileResponse.Builder responseBuilder = GetUnversionedProfileResponse.newBuilder() final GetUnversionedProfileResponse.Builder responseBuilder = GetUnversionedProfileResponse.newBuilder()
.setIdentityKey(ByteString.copyFrom(targetAccount.getIdentityKey(targetIdentifier.identityType()).serialize())) .setIdentityKey(ByteString.copyFrom(targetAccount.getIdentityKey(targetIdentifier.identityType()).serialize()))
.setCapabilities(buildUserCapabilities(org.whispersystems.textsecuregcm.entities.UserCapabilities.createForAccount(targetAccount))); .setCapabilities(buildUserCapabilities(org.whispersystems.textsecuregcm.entities.UserCapabilities.createForAccount(targetAccount, true)));
switch (targetIdentifier.identityType()) { switch (targetIdentifier.identityType()) {
case ACI -> { case ACI -> {

View File

@ -88,6 +88,7 @@ import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialProfileResponse; import org.whispersystems.textsecuregcm.entities.ExpiringProfileKeyCredentialProfileResponse;
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes; import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse; import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier; import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.identity.IdentityType;
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier; import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
@ -157,6 +158,7 @@ class ProfileControllerTest {
accountsManager, accountsManager,
profilesManager, profilesManager,
dynamicConfigurationManager, dynamicConfigurationManager,
mock(ExperimentEnrollmentManager.class),
(acceptableLanguages, accountBadges, isSelf) -> List.of(new Badge("TEST", "other", "Test Badge", (acceptableLanguages, accountBadges, isSelf) -> List.of(new Badge("TEST", "other", "Test Badge",
"This badge is in unit tests.", List.of("l", "m", "h", "x", "xx", "xxx"), "SVG", List.of(new BadgeSvg("sl", "sd"), new BadgeSvg("ml", "md"), new BadgeSvg("ll", "ld"))) "This badge is in unit tests.", List.of("l", "m", "h", "x", "xx", "xxx"), "SVG", List.of(new BadgeSvg("sl", "sd"), new BadgeSvg("ml", "md"), new BadgeSvg("ll", "ld")))
), ),

View File

@ -162,7 +162,7 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest<ProfileA
.setIdentityKey(ByteString.copyFrom(identityKey.serialize())) .setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
.setUnidentifiedAccess(ByteString.copyFrom(unidentifiedAccessChecksum)) .setUnidentifiedAccess(ByteString.copyFrom(unidentifiedAccessChecksum))
.setUnrestrictedUnidentifiedAccess(false) .setUnrestrictedUnidentifiedAccess(false)
.setCapabilities(ProfileGrpcHelper.buildUserCapabilities(UserCapabilities.createForAccount(account))) .setCapabilities(ProfileGrpcHelper.buildUserCapabilities(UserCapabilities.createForAccount(account, true)))
.addAllBadges(ProfileGrpcHelper.buildBadges(badges)) .addAllBadges(ProfileGrpcHelper.buildBadges(badges))
.build(); .build();

View File

@ -429,7 +429,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic
.setIdentityKey(ByteString.copyFrom(identityKey.serialize())) .setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
.setUnidentifiedAccess(ByteString.copyFrom(unidentifiedAccessChecksum)) .setUnidentifiedAccess(ByteString.copyFrom(unidentifiedAccessChecksum))
.setUnrestrictedUnidentifiedAccess(true) .setUnrestrictedUnidentifiedAccess(true)
.setCapabilities(ProfileGrpcHelper.buildUserCapabilities(UserCapabilities.createForAccount(account))) .setCapabilities(ProfileGrpcHelper.buildUserCapabilities(UserCapabilities.createForAccount(account, true)))
.addAllBadges(ProfileGrpcHelper.buildBadges(badges)) .addAllBadges(ProfileGrpcHelper.buildBadges(badges))
.build(); .build();