Use login email instead of GAE user ID for RegistrarPoc (#1852)

Switch to using the login email address instead of GAE user ID to
identify console users. The primary use cases are:

1) When the user logged in the registrar console, need to figure out
   which registrars they have access to (in
   AuthenticatedReigstrarAccess).

2) When a user tries to apply a registry lock, needs to know if they
   can (in RegistryLockGetAction).

Both cases are tested in alpha with a personal email address to ensure
it does not get the permission due to being a GAE admin account.

Also verified that the soy templates includes the hidden login email
form field instead of GAE user ID when registrars are displayed on the
console; and consequently when a contact update is posted to the server,
the login email is part of the JSON payload. Even though it does not
look like it is used in any way by RegistrarSettingsAction, which
receives the POST request. Like GAE user ID, the field is hidden, so
cannot be changed by the user from the console, it is also not used to
identify the RegistryPoc entity, whose composite keys are the contact
email and the registrar ID associated with it.

The login email address is backfilled for all RegistrarPocs that have a
non-null GAE user ID. The backfilled addresses converted to the same ID
as stored in the database.
This commit is contained in:
Lai Jiang
2022-11-29 17:16:19 -05:00
committed by GitHub
parent e3944d5d52
commit 85272a30a2
34 changed files with 231 additions and 259 deletions
@@ -15,7 +15,6 @@
package google.registry.model;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY;
@@ -30,7 +29,6 @@ import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import google.registry.config.RegistryEnvironment;
import google.registry.model.common.GaeUserIdConverter;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
@@ -77,14 +75,14 @@ public final class OteAccountBuilder {
* Validation regex for registrar base client IDs (3-14 lowercase alphanumeric characters).
*
* <p>The base client ID is appended with numbers to create four different test registrar accounts
* (e.g. reg-1, reg-3, reg-4, reg-5). Registrar client IDs are of type clIDType in eppcom.xsd
* (e.g. reg-1, reg-3, reg-4, reg-5). Registrar client IDs are of type clIDType in eppcom.xsd
* which is limited to 16 characters, hence the limit of 14 here to account for the dash and
* numbers.
*
* <p>The base client ID is also used to generate the OT&E TLDs, hence the restriction to
* lowercase alphanumeric characters.
*/
private static final Pattern REGISTRAR_PATTERN = Pattern.compile("^[a-z0-9]{3,14}$");
private static final Pattern REGISTRAR_PATTERN = Pattern.compile("^[a-z\\d]{3,14}$");
// Durations are short so that registrars can test with quick transfer (etc.) turnaround.
private static final Duration SHORT_ADD_GRACE_PERIOD = Duration.standardMinutes(60);
@@ -179,17 +177,11 @@ public final class OteAccountBuilder {
* <p>NOTE: can be called more than once, adding multiple contacts. Each contact will have access
* to all OT&amp;E Registrars.
*
* @param email the contact email that will have web-console access to all the Registrars. Must be
* from "our G Suite domain" (we have to be able to get its GaeUserId)
* @param email the contact/login email that will have web-console access to all the Registrars.
* Must be from "our G Suite domain".
*/
public OteAccountBuilder addContact(String email) {
String gaeUserId =
checkNotNull(
GaeUserIdConverter.convertEmailAddressToGaeUserId(email),
"Email address %s is not associated with any GAE ID",
email);
registrars.forEach(
registrar -> contactsBuilder.add(createRegistrarContact(email, gaeUserId, registrar)));
registrars.forEach(registrar -> contactsBuilder.add(createRegistrarContact(email, registrar)));
return this;
}
@@ -304,7 +296,7 @@ public final class OteAccountBuilder {
TldState initialTldState,
boolean isEarlyAccess,
int roidSuffix) {
String tldNameAlphaNumerical = tldName.replaceAll("[^a-z0-9]", "");
String tldNameAlphaNumerical = tldName.replaceAll("[^a-z\\d]", "");
Optional<PremiumList> premiumList = PremiumListDao.getLatestRevision(DEFAULT_PREMIUM_LIST);
checkState(premiumList.isPresent(), "Couldn't find premium list %s.", DEFAULT_PREMIUM_LIST);
Registry.Builder builder =
@@ -348,13 +340,12 @@ public final class OteAccountBuilder {
.build();
}
private static RegistrarPoc createRegistrarContact(
String email, String gaeUserId, Registrar registrar) {
private static RegistrarPoc createRegistrarContact(String email, Registrar registrar) {
return new RegistrarPoc.Builder()
.setRegistrar(registrar)
.setName(email)
.setEmailAddress(email)
.setGaeUserId(gaeUserId)
.setLoginEmailAddress(email)
.build();
}
@@ -57,7 +57,7 @@ import javax.persistence.Table;
* set to true.
*/
@Entity
@Table(indexes = {@Index(columnList = "gaeUserId", name = "registrarpoc_gae_user_id_idx")})
@Table(indexes = @Index(columnList = "loginEmailAddress", name = "registrarpoc_login_email_idx"))
@IdClass(RegistrarPocId.class)
public class RegistrarPoc extends ImmutableObject implements Jsonifiable, UnsafeSerializable {
@@ -89,7 +89,7 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
}
Type(String display, boolean required) {
this.displayName = display;
displayName = display;
this.required = required;
}
}
@@ -97,7 +97,12 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
/** The name of the contact. */
String name;
/** The email address of the contact. */
/**
* The contact email address of the contact.
*
* <p>This is different from the login email which is assgined to the regstrar and cannot be
* changed.
*/
@Id String emailAddress;
@Id String registrarId;
@@ -118,13 +123,21 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
Set<Type> types;
/**
* A GAE user ID allowed to act as this registrar contact.
* A GAIA email address that was assigned to the registrar for console login purpose.
*
* <p>This can be derived from a known email address using http://email-to-gae-id.appspot.com.
* <p>We used to store the GAE user ID directly to identify the logged-in user in the registrar
* console, and relied on a hacky trick with datastore to get the ID from the email address when
* creating a {@link RegistrarPoc}. We switched to using the login email directly as each
* registrar is assigned a unique email address that is immutable (to them at least), so it is as
* good as an identifier as the ID itself, and it allows us to get rid of the datastore
* dependency.
*
* @see com.google.appengine.api.users.User#getUserId()
* <p>We backfilled all login email addresses for existing {@link RegistrarPoc}s that have a
* non-null GAE user ID. The backfill is done by first trying the {@link #emailAddress} field,
* then trying {@link #registrarId}+"@known-dasher_domain" and picking the ones that converted to
* the existing ID stored in the database.
*/
String gaeUserId;
String loginEmailAddress;
/**
* Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact.
@@ -220,8 +233,8 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
return visibleInDomainWhoisAsAbuse;
}
public String getGaeUserId() {
return gaeUserId;
public String getLoginEmailAddress() {
return loginEmailAddress;
}
public Builder asBuilder() {
@@ -258,8 +271,8 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
* Types: [ADMIN, WHOIS]
* Visible in WHOIS as Admin contact: Yes
* Visible in WHOIS as Technical contact: No
* GAE-UserID: 1234567890
* Registrar-Console access: Yes
* Login Email Address: person@registry.example
* }</pre>
*/
public String toStringMultilinePlainText() {
@@ -289,10 +302,10 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
.append('\n');
result
.append("Registrar-Console access: ")
.append(getGaeUserId() != null ? "Yes" : "No")
.append(getLoginEmailAddress() != null ? "Yes" : "No")
.append('\n');
if (getGaeUserId() != null) {
result.append("GAE-UserID: ").append(getGaeUserId()).append('\n');
if (getLoginEmailAddress() != null) {
result.append("Login Email Address: ").append(getLoginEmailAddress()).append('\n');
}
return result.toString();
}
@@ -311,7 +324,7 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)
.put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword)
.put("registryLockAllowed", isRegistryLockAllowed())
.put("gaeUserId", gaeUserId)
.put("loginEmailAddress", loginEmailAddress)
.build();
}
@@ -418,8 +431,8 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
return this;
}
public Builder setGaeUserId(String gaeUserId) {
getInstance().gaeUserId = gaeUserId;
public Builder setLoginEmailAddress(String loginEmailAddress) {
getInstance().loginEmailAddress = loginEmailAddress;
return this;
}
@@ -21,6 +21,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.google.appengine.api.users.User;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.FluentLogger;
@@ -307,7 +308,7 @@ public class AuthenticatedRegistrarAccessor {
UserAuthInfo userAuthInfo = authResult.userAuthInfo().get();
if (userAuthInfo.appEngineUser().isPresent()) {
User user = userAuthInfo.appEngineUser().get();
logger.atInfo().log("Checking registrar contacts for user ID %s.", user.getUserId());
logger.atInfo().log("Checking registrar contacts for user ID %s.", user.getEmail());
// Find all registrars that have a registrar contact with this user's ID.
jpaTm()
@@ -315,11 +316,11 @@ public class AuthenticatedRegistrarAccessor {
() ->
jpaTm()
.query(
"SELECT r FROM Registrar r INNER JOIN RegistrarPoc rp ON "
+ "r.registrarId = rp.registrarId WHERE rp.gaeUserId = "
+ ":gaeUserId AND r.state != :state",
"SELECT r FROM Registrar r INNER JOIN RegistrarPoc rp ON r.registrarId ="
+ " rp.registrarId WHERE lower(rp.loginEmailAddress) = :email AND"
+ " r.state != :state",
Registrar.class)
.setParameter("gaeUserId", user.getUserId())
.setParameter("email", Ascii.toLowerCase(user.getEmail()))
.setParameter("state", State.DISABLED)
.getResultStream()
.forEach(registrar -> builder.put(registrar.getRegistrarId(), Role.OWNER)));
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -29,7 +28,6 @@ import com.google.common.base.Enums;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.common.GaeUserIdConverter;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.tools.params.OptionalPhoneNumberParameter;
@@ -48,6 +46,7 @@ import java.util.Set;
import javax.annotation.Nullable;
/** Command for CRUD operations on {@link Registrar} contact list fields. */
@SuppressWarnings("OptionalAssignedToNull")
@Parameters(
separators = " =",
commandDescription = "Create/read/update/delete the various contact lists for a Registrar.")
@@ -78,9 +77,19 @@ final class RegistrarPocCommand extends MutatingCommand {
private List<String> contactTypeNames;
@Nullable
@Parameter(names = "--email", description = "Contact email address.")
@Parameter(
names = "--email",
description =
"Contact email address. Required when creating a contact"
+ " and will be used as the console login email, if --login_email is not specified.")
String email;
@Nullable
@Parameter(
names = "--login_email",
description = "Console login email address. If not specified, --email will be used.")
String loginEmail;
@Nullable
@Parameter(
names = "--registry_lock_email",
@@ -168,13 +177,13 @@ final class RegistrarPocCommand extends MutatingCommand {
// If the contact_type parameter is not specified, we should not make any changes.
if (contactTypeNames == null) {
contactTypes = null;
// It appears that when the user specifies "--contact_type=" with no types following, JCommander
// sets contactTypeNames to a one-element list containing the empty string. This is strange, but
// we need to handle this by setting the contact types to the empty set. Also do this if
// contactTypeNames is empty, which is what I would hope JCommander would return in some future,
// better world.
// It appears that when the user specifies "--contact_type=" with no types following,
// JCommander sets contactTypeNames to a one-element list containing the empty string. This is
// strange, but we need to handle this by setting the contact types to the empty set. Also do
// this if contactTypeNames is empty, which is what I would hope JCommander would return in
// some future, better world.
} else if (contactTypeNames.isEmpty()
|| ((contactTypeNames.size() == 1) && contactTypeNames.get(0).isEmpty())) {
|| contactTypeNames.size() == 1 && contactTypeNames.get(0).isEmpty()) {
contactTypes = ImmutableSet.of();
} else {
contactTypes =
@@ -194,7 +203,7 @@ final class RegistrarPocCommand extends MutatingCommand {
break;
case CREATE:
stageEntityChange(null, createContact(registrar));
if ((visibleInDomainWhoisAsAbuse != null) && visibleInDomainWhoisAsAbuse) {
if (visibleInDomainWhoisAsAbuse != null && visibleInDomainWhoisAsAbuse) {
unsetOtherWhoisAbuseFlags(contacts, null);
}
break;
@@ -211,7 +220,7 @@ final class RegistrarPocCommand extends MutatingCommand {
"Cannot clear visible_in_domain_whois_as_abuse flag, as that would leave no domain"
+ " WHOIS abuse contacts; instead, set the flag on another contact");
stageEntityChange(oldContact, newContact);
if ((visibleInDomainWhoisAsAbuse != null) && visibleInDomainWhoisAsAbuse) {
if (visibleInDomainWhoisAsAbuse != null && visibleInDomainWhoisAsAbuse) {
unsetOtherWhoisAbuseFlags(contacts, oldContact.getEmailAddress());
}
break;
@@ -261,9 +270,7 @@ final class RegistrarPocCommand extends MutatingCommand {
builder.setTypes(nullToEmpty(contactTypes));
if (Objects.equals(allowConsoleAccess, Boolean.TRUE)) {
builder.setGaeUserId(checkArgumentNotNull(
GaeUserIdConverter.convertEmailAddressToGaeUserId(email),
String.format("Email address %s is not associated with any GAE ID", email)));
builder.setLoginEmailAddress(loginEmail == null ? email : loginEmail);
}
if (visibleInWhoisAsAdmin != null) {
builder.setVisibleInWhoisAsAdmin(visibleInWhoisAsAdmin);
@@ -311,11 +318,9 @@ final class RegistrarPocCommand extends MutatingCommand {
}
if (allowConsoleAccess != null) {
if (allowConsoleAccess.equals(Boolean.TRUE)) {
builder.setGaeUserId(checkArgumentNotNull(
GaeUserIdConverter.convertEmailAddressToGaeUserId(email),
String.format("Email address %s is not associated with any GAE ID", email)));
builder.setLoginEmailAddress(loginEmail == null ? email : loginEmail);
} else {
builder.setGaeUserId(null);
builder.setLoginEmailAddress(null);
}
}
if (allowedToSetRegistryLockPassword != null) {
@@ -206,8 +206,8 @@ public final class RegistrarFormFields {
public static final FormField<String, String> CONTACT_FAX_NUMBER_FIELD =
FormFields.PHONE_NUMBER.asBuilderNamed("faxNumber").build();
public static final FormField<String, String> CONTACT_GAE_USER_ID_FIELD =
FormFields.NAME.asBuilderNamed("gaeUserId").build();
public static final FormField<String, String> CONTACT_LOGIN_EMAIL_ADDRESS_FIELD =
FormFields.NAME.asBuilderNamed("loginEmailAddress").build();
public static final FormField<Object, Boolean> CONTACT_ALLOWED_TO_SET_REGISTRY_LOCK_PASSWORD =
FormField.named("allowedToSetRegistryLockPassword", Object.class)
@@ -284,11 +284,15 @@ public final class RegistrarFormFields {
public static final FormField<Map<String, ?>, RegistrarAddress> L10N_ADDRESS_FIELD =
FormField.mapNamed("localizedAddress")
.transform(RegistrarAddress.class, (args) -> toNewAddress(
args, L10N_STREET_FIELD, L10N_CITY_FIELD, L10N_STATE_FIELD, L10N_ZIP_FIELD))
.transform(
RegistrarAddress.class,
args ->
toNewAddress(
args, L10N_STREET_FIELD, L10N_CITY_FIELD, L10N_STATE_FIELD, L10N_ZIP_FIELD))
.build();
private static @Nullable RegistrarAddress toNewAddress(
@Nullable
private static RegistrarAddress toNewAddress(
@Nullable Map<String, ?> args,
final FormField<List<String>, List<String>> streetField,
final FormField<String, String> cityField,
@@ -327,7 +331,8 @@ public final class RegistrarFormFields {
}
}
private static @Nullable String parseHostname(@Nullable String input) {
@Nullable
private static String parseHostname(@Nullable String input) {
if (input == null) {
return null;
}
@@ -337,7 +342,8 @@ public final class RegistrarFormFields {
return canonicalizeHostname(input);
}
public static @Nullable DateTime parseDateTime(@Nullable String input) {
@Nullable
public static DateTime parseDateTime(@Nullable String input) {
if (input == null) {
return null;
}
@@ -391,7 +397,8 @@ public final class RegistrarFormFields {
builder.setPhoneNumber(CONTACT_PHONE_NUMBER_FIELD.extractUntyped(args).orElse(null));
builder.setFaxNumber(CONTACT_FAX_NUMBER_FIELD.extractUntyped(args).orElse(null));
builder.setTypes(CONTACT_TYPES.extractUntyped(args).orElse(ImmutableSet.of()));
builder.setGaeUserId(CONTACT_GAE_USER_ID_FIELD.extractUntyped(args).orElse(null));
builder.setLoginEmailAddress(
CONTACT_LOGIN_EMAIL_ADDRESS_FIELD.extractUntyped(args).orElse(null));
// The parser is inconsistent with whether it retrieves boolean values as strings or booleans.
// As a result, use a potentially-redundant converter that can deal with both.
builder.setAllowedToSetRegistryLockPassword(
@@ -14,36 +14,40 @@
package google.registry.ui.server.registrar;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static google.registry.model.common.GaeUserIdConverter.convertEmailAddressToGaeUserId;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import com.google.common.base.Ascii;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.template.soy.tofu.SoyTofu;
import google.registry.config.RegistryEnvironment;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.request.Action;
import google.registry.request.Action.Method;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.ui.server.SendEmailUtils;
import google.registry.ui.server.SoyTemplateUtils;
import google.registry.ui.soy.registrar.AnalyticsSoyInfo;
import google.registry.ui.soy.registrar.ConsoleSoyInfo;
import google.registry.ui.soy.registrar.ConsoleUtilsSoyInfo;
import google.registry.ui.soy.registrar.FormsSoyInfo;
import google.registry.ui.soy.registrar.RegistrarCreateConsoleSoyInfo;
import google.registry.util.StringGenerator;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Named;
@@ -54,12 +58,9 @@ import org.joda.money.CurrencyUnit;
*
* <p>This Action does 2 things: - for GET, just returns the form that asks for the required
* information. - for POST, receives the information and creates the Registrar.
*
* <p>TODO(b/120201577): once we can have 2 different Actions with the same path (different
* Methods), separate this class to 2 Actions.
*/
@Action(
service = Action.Service.DEFAULT,
service = Service.DEFAULT,
path = ConsoleRegistrarCreatorAction.PATH,
method = {Method.POST, Method.GET},
auth = Auth.AUTH_PUBLIC)
@@ -74,11 +75,11 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
private static final Supplier<SoyTofu> TOFU_SUPPLIER =
SoyTemplateUtils.createTofuSupplier(
google.registry.ui.soy.registrar.AnalyticsSoyInfo.getInstance(),
google.registry.ui.soy.registrar.ConsoleSoyInfo.getInstance(),
google.registry.ui.soy.registrar.ConsoleUtilsSoyInfo.getInstance(),
google.registry.ui.soy.registrar.FormsSoyInfo.getInstance(),
google.registry.ui.soy.registrar.RegistrarCreateConsoleSoyInfo.getInstance());
AnalyticsSoyInfo.getInstance(),
ConsoleSoyInfo.getInstance(),
ConsoleUtilsSoyInfo.getInstance(),
FormsSoyInfo.getInstance(),
RegistrarCreateConsoleSoyInfo.getInstance());
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
@Inject SendEmailUtils sendEmailUtils;
@@ -136,7 +137,7 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
return PATH;
}
private void checkPresent(Optional<?> value, String name) {
private static void checkPresent(Optional<?> value, String name) {
checkState(value.isPresent(), "Missing value for %s", name);
}
@@ -193,11 +194,6 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
optionalZip.ifPresent(zip -> data.put("zip", zip));
data.put("countryCode", countryCode.get());
String gaeUserId =
checkNotNull(
convertEmailAddressToGaeUserId(consoleUserEmail.get()),
"Email address %s is not associated with any GAE ID",
consoleUserEmail.get());
String password = optionalPassword.orElse(passwordGenerator.createString(PASSWORD_LENGTH));
String phonePasscode =
optionalPasscode.orElse(passcodeGenerator.createString(PASSCODE_LENGTH));
@@ -213,7 +209,7 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
.setType(Registrar.Type.REAL)
.setPassword(password)
.setPhonePasscode(phonePasscode)
.setState(Registrar.State.PENDING)
.setState(State.PENDING)
.setLocalizedAddress(
new RegistrarAddress.Builder()
.setStreet(
@@ -232,7 +228,7 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
.setRegistrar(registrar)
.setName(consoleUserEmail.get())
.setEmailAddress(consoleUserEmail.get())
.setGaeUserId(gaeUserId)
.setLoginEmailAddress(consoleUserEmail.get())
.build();
tm().transact(
() -> {
@@ -285,7 +281,7 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
.render());
}
private String toEmailLine(Optional<?> value, String name) {
private static String toEmailLine(Optional<?> value, String name) {
return String.format(" %s: %s\n", name, value.orElse(null));
}
private void sendExternalUpdates() {
@@ -24,6 +24,7 @@ import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import com.google.appengine.api.users.User;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
@@ -123,16 +124,21 @@ public final class RegistryLockGetAction implements JsonGetAction {
static Optional<RegistrarPoc> getContactMatchingLogin(User user, Registrar registrar) {
ImmutableList<RegistrarPoc> matchingContacts =
registrar.getContacts().stream()
.filter(contact -> contact.getGaeUserId() != null)
.filter(contact -> Objects.equals(contact.getGaeUserId(), user.getUserId()))
.filter(contact -> contact.getLoginEmailAddress() != null)
.filter(
contact ->
Objects.equals(
Ascii.toLowerCase(contact.getLoginEmailAddress()),
Ascii.toLowerCase(user.getEmail())))
.collect(toImmutableList());
if (matchingContacts.size() > 1) {
ImmutableList<String> matchingEmails =
matchingContacts.stream().map(RegistrarPoc::getEmailAddress).collect(toImmutableList());
throw new IllegalArgumentException(
String.format(
"User ID %s had multiple matching contacts with email addresses %s",
user.getUserId(), matchingEmails));
"User with login email %s had multiple matching contacts with contact email addresses"
+ " %s",
user.getEmail(), matchingEmails));
}
return matchingContacts.stream().findFirst();
}
@@ -188,7 +194,7 @@ public final class RegistryLockGetAction implements JsonGetAction {
getLockedDomains(registrarId, isAdmin));
}
private ImmutableList<ImmutableMap<String, ?>> getLockedDomains(
private static ImmutableList<ImmutableMap<String, ?>> getLockedDomains(
String registrarId, boolean isAdmin) {
return jpaTm()
.transact(
@@ -199,7 +205,7 @@ public final class RegistryLockGetAction implements JsonGetAction {
.collect(toImmutableList()));
}
private ImmutableMap<String, ?> lockToMap(RegistryLock lock, boolean isAdmin) {
private static ImmutableMap<String, ?> lockToMap(RegistryLock lock, boolean isAdmin) {
DateTime now = jpaTm().getTransactionTime();
return new ImmutableMap.Builder<String, Object>()
.put(DOMAIN_NAME_PARAM, lock.getDomainName())
@@ -183,8 +183,8 @@
{/call}
{/if}
</table>
{if isNonnull($item['gaeUserId'])}
<input type="hidden" name="{$namePrefix}gaeUserId" value="{$item['gaeUserId']}">
{if isNonnull($item['loginEmailAddress'])}
<input type="hidden" name="{$namePrefix}loginEmailAddress" value="{$item['loginEmailAddress']}">
{/if}
</div>
{/template}