From 9aa7b699216f727fc4a2752bfaeb142a0551c66d Mon Sep 17 00:00:00 2001 From: guyben Date: Wed, 16 Jan 2019 21:39:49 -0800 Subject: [PATCH] Add web console for creating registrars This console is only to be used by Admins (either GAE admins for this project, or Support accounts). It is for "internal" use only, not for use by the registrars themselves. To prevent abuse, the registrar is created in a non-functional PENDING state and can only be made functional from the nomulus shell tool. While in "PENDING" state, the registrar can be updated from the registrar-console by admins. Also - moving all the web consoles to the same directory (moving the otesetup/* files into registrar/) ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=229681011 --- gradle/core/build.gradle | 6 - .../registry/config/RegistryConfig.java | 8 + .../env/common/default/WEB-INF/web.xml | 6 + java/google/registry/module/frontend/BUILD | 1 - .../frontend/FrontendRequestComponent.java | 4 +- .../auth/AuthenticatedRegistrarAccessor.java | 3 +- java/google/registry/ui/server/otesetup/BUILD | 43 -- .../ConsoleOteSetupAction.java | 15 +- .../ConsoleRegistrarCreatorAction.java | 369 ++++++++++++++++ .../registrar/RegistrarConsoleModule.java | 79 ++++ java/google/registry/ui/soy/otesetup/BUILD | 21 - java/google/registry/ui/soy/registrar/BUILD | 8 +- .../OteSetupConsole.soy} | 4 +- .../soy/registrar/RegistrarCreateConsole.soy | 360 +++++++++++++++ .../google/registry/util/StringGenerator.java | 3 + .../frontend/testdata/frontend_routing.txt | 15 +- .../registry/server/RegistryTestServer.java | 3 +- .../google/registry/ui/server/otesetup/BUILD | 45 -- .../ConsoleOteSetupActionTest.java | 5 +- .../ConsoleRegistrarCreatorActionTest.java | 417 ++++++++++++++++++ 20 files changed, 1277 insertions(+), 138 deletions(-) delete mode 100644 java/google/registry/ui/server/otesetup/BUILD rename java/google/registry/ui/server/{otesetup => registrar}/ConsoleOteSetupAction.java (95%) create mode 100644 java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java delete mode 100644 java/google/registry/ui/soy/otesetup/BUILD rename java/google/registry/ui/soy/{otesetup/Console.soy => registrar/OteSetupConsole.soy} (98%) create mode 100644 java/google/registry/ui/soy/registrar/RegistrarCreateConsole.soy delete mode 100644 javatests/google/registry/ui/server/otesetup/BUILD rename javatests/google/registry/ui/server/{otesetup => registrar}/ConsoleOteSetupActionTest.java (98%) create mode 100644 javatests/google/registry/ui/server/registrar/ConsoleRegistrarCreatorActionTest.java diff --git a/gradle/core/build.gradle b/gradle/core/build.gradle index a7ffd3e60..21f520db8 100644 --- a/gradle/core/build.gradle +++ b/gradle/core/build.gradle @@ -341,12 +341,6 @@ task soyToJava { dir: "${javaDir}/google/registry/ui/soy/registrar", include: ['**/*.soy'])) - soyToJava('google.registry.ui.soy.otesetup', - "${generatedDir}/google/registry/ui/soy/otesetup", - fileTree( - dir: "${javaDir}/google/registry/ui/soy/otesetup", - include: ['**/*.soy'])) - soyToJava('google.registry.ui.soy', "${generatedDir}/google/registry/ui/soy", files { diff --git a/java/google/registry/config/RegistryConfig.java b/java/google/registry/config/RegistryConfig.java index a2299f917..f3bc7bf1d 100644 --- a/java/google/registry/config/RegistryConfig.java +++ b/java/google/registry/config/RegistryConfig.java @@ -1381,6 +1381,14 @@ public final class RegistryConfig { } } + /** Returns a singleton random string generator that uses digits only. */ + @Singleton + @Provides + @Config("digitOnlyStringGenerator") + public static StringGenerator provideDigitsOnlyStringGenerator(SecureRandom secureRandom) { + return new RandomStringGenerator(StringGenerator.Alphabets.DIGITS_ONLY, secureRandom); + } + /** Returns a singleton random string generator using Base58 encoding. */ @Singleton @Provides diff --git a/java/google/registry/env/common/default/WEB-INF/web.xml b/java/google/registry/env/common/default/WEB-INF/web.xml index bcbc77703..5861ad0c1 100644 --- a/java/google/registry/env/common/default/WEB-INF/web.xml +++ b/java/google/registry/env/common/default/WEB-INF/web.xml @@ -43,6 +43,12 @@ /registrar-ote-setup + + + frontend-servlet + /registrar-create + + diff --git a/java/google/registry/module/frontend/BUILD b/java/google/registry/module/frontend/BUILD index fc2d681fc..796bee607 100644 --- a/java/google/registry/module/frontend/BUILD +++ b/java/google/registry/module/frontend/BUILD @@ -21,7 +21,6 @@ java_library( "//java/google/registry/request:modules", "//java/google/registry/request/auth", "//java/google/registry/ui", - "//java/google/registry/ui/server/otesetup", "//java/google/registry/ui/server/registrar", "//java/google/registry/util", "@com_google_appengine_api_1_0_sdk", diff --git a/java/google/registry/module/frontend/FrontendRequestComponent.java b/java/google/registry/module/frontend/FrontendRequestComponent.java index 277f2d745..42743cb83 100644 --- a/java/google/registry/module/frontend/FrontendRequestComponent.java +++ b/java/google/registry/module/frontend/FrontendRequestComponent.java @@ -25,7 +25,8 @@ import google.registry.monitoring.whitebox.WhiteboxModule; import google.registry.request.RequestComponentBuilder; import google.registry.request.RequestModule; import google.registry.request.RequestScope; -import google.registry.ui.server.otesetup.ConsoleOteSetupAction; +import google.registry.ui.server.registrar.ConsoleOteSetupAction; +import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction; import google.registry.ui.server.registrar.ConsoleUiAction; import google.registry.ui.server.registrar.OteStatusAction; import google.registry.ui.server.registrar.RegistrarConsoleModule; @@ -43,6 +44,7 @@ import google.registry.ui.server.registrar.RegistrarSettingsAction; }) interface FrontendRequestComponent { ConsoleOteSetupAction consoleOteSetupAction(); + ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction(); ConsoleUiAction consoleUiAction(); EppConsoleAction eppConsoleAction(); EppTlsAction eppTlsAction(); diff --git a/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java b/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java index c1d080730..51ec7415b 100644 --- a/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java +++ b/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java @@ -329,7 +329,8 @@ public class AuthenticatedRegistrarAccessor { .load() .type(Registrar.class) .forEach(registrar -> { - if (!Registrar.Type.REAL.equals(registrar.getType())) { + if (registrar.getType() != Registrar.Type.REAL + || registrar.getState() == Registrar.State.PENDING) { builder.put(registrar.getClientId(), Role.OWNER); } builder.put(registrar.getClientId(), Role.ADMIN); diff --git a/java/google/registry/ui/server/otesetup/BUILD b/java/google/registry/ui/server/otesetup/BUILD deleted file mode 100644 index 4ce526598..000000000 --- a/java/google/registry/ui/server/otesetup/BUILD +++ /dev/null @@ -1,43 +0,0 @@ -package( - default_visibility = ["//visibility:public"], -) - -licenses(["notice"]) # Apache 2.0 - -java_library( - name = "otesetup", - srcs = glob(["*.java"]), - resources = [ - "//java/google/registry/ui/css:registrar_bin.css.js", - "//java/google/registry/ui/css:registrar_dbg.css.js", - ], - deps = [ - "//java/google/registry/config", - "//java/google/registry/export/sheet", - "//java/google/registry/flows", - "//java/google/registry/model", - "//java/google/registry/request", - "//java/google/registry/request/auth", - "//java/google/registry/security", - "//java/google/registry/ui/forms", - "//java/google/registry/ui/server", - "//java/google/registry/ui/soy:soy_java_wrappers", - "//java/google/registry/ui/soy/otesetup:soy_java_wrappers", - "//java/google/registry/util", - "//third_party/objectify:objectify-v4_1", - "@com_google_appengine_api_1_0_sdk", - "@com_google_auto_value", - "@com_google_code_findbugs_jsr305", - "@com_google_dagger", - "@com_google_flogger", - "@com_google_flogger_system_backend", - "@com_google_guava", - "@com_google_monitoring_client_metrics", - "@com_google_re2j", - "@io_bazel_rules_closure//closure/templates", - "@javax_inject", - "@javax_servlet_api", - "@joda_time", - "@org_joda_money", - ], -) diff --git a/java/google/registry/ui/server/otesetup/ConsoleOteSetupAction.java b/java/google/registry/ui/server/registrar/ConsoleOteSetupAction.java similarity index 95% rename from java/google/registry/ui/server/otesetup/ConsoleOteSetupAction.java rename to java/google/registry/ui/server/registrar/ConsoleOteSetupAction.java index ee553f345..96ad6b292 100644 --- a/java/google/registry/ui/server/otesetup/ConsoleOteSetupAction.java +++ b/java/google/registry/ui/server/registrar/ConsoleOteSetupAction.java @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -package google.registry.ui.server.otesetup; +package google.registry.ui.server.registrar; + import static com.google.common.base.Preconditions.checkState; import static com.google.common.net.HttpHeaders.LOCATION; import static com.google.common.net.HttpHeaders.X_FRAME_OPTIONS; @@ -44,7 +45,7 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.security.XsrfTokenManager; import google.registry.ui.server.SendEmailUtils; import google.registry.ui.server.SoyTemplateUtils; -import google.registry.ui.soy.otesetup.ConsoleSoyInfo; +import google.registry.ui.soy.registrar.OteSetupConsoleSoyInfo; import google.registry.util.StringGenerator; import java.util.HashMap; import java.util.Optional; @@ -77,7 +78,7 @@ public final class ConsoleOteSetupAction implements Runnable { SoyTemplateUtils.createTofuSupplier( google.registry.ui.soy.ConsoleSoyInfo.getInstance(), google.registry.ui.soy.FormsSoyInfo.getInstance(), - google.registry.ui.soy.otesetup.ConsoleSoyInfo.getInstance()); + google.registry.ui.soy.registrar.OteSetupConsoleSoyInfo.getInstance()); @VisibleForTesting // webdriver and screenshot tests need this public static final Supplier CSS_RENAMING_MAP_SUPPLIER = @@ -145,7 +146,7 @@ public final class ConsoleOteSetupAction implements Runnable { response.setPayload( TOFU_SUPPLIER .get() - .newRenderer(ConsoleSoyInfo.WHOAREYOU) + .newRenderer(OteSetupConsoleSoyInfo.WHOAREYOU) .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) .setData(data) .render()); @@ -185,7 +186,7 @@ public final class ConsoleOteSetupAction implements Runnable { response.setPayload( TOFU_SUPPLIER .get() - .newRenderer(ConsoleSoyInfo.RESULT_SUCCESS) + .newRenderer(OteSetupConsoleSoyInfo.RESULT_SUCCESS) .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) .setData(data) .render()); @@ -196,7 +197,7 @@ public final class ConsoleOteSetupAction implements Runnable { response.setPayload( TOFU_SUPPLIER .get() - .newRenderer(ConsoleSoyInfo.FORM_PAGE) + .newRenderer(OteSetupConsoleSoyInfo.FORM_PAGE) .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) .setData(data) .render()); @@ -211,7 +212,7 @@ public final class ConsoleOteSetupAction implements Runnable { response.setPayload( TOFU_SUPPLIER .get() - .newRenderer(ConsoleSoyInfo.FORM_PAGE) + .newRenderer(OteSetupConsoleSoyInfo.FORM_PAGE) .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) .setData(data) .render()); diff --git a/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java b/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java new file mode 100644 index 000000000..de79bc103 --- /dev/null +++ b/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java @@ -0,0 +1,369 @@ +// Copyright 2018 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +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 com.google.common.net.HttpHeaders.LOCATION; +import static com.google.common.net.HttpHeaders.X_FRAME_OPTIONS; +import static google.registry.model.common.GaeUserIdConverter.convertEmailAddressToGaeUserId; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY; + +import com.google.appengine.api.users.User; +import com.google.appengine.api.users.UserService; +import com.google.common.annotations.VisibleForTesting; +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.common.io.Resources; +import com.google.common.net.MediaType; +import com.google.template.soy.shared.SoyCssRenamingMap; +import com.google.template.soy.tofu.SoyTofu; +import google.registry.config.RegistryConfig.Config; +import google.registry.config.RegistryEnvironment; +import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.RegistrarAddress; +import google.registry.model.registrar.RegistrarContact; +import google.registry.request.Action; +import google.registry.request.Action.Method; +import google.registry.request.Parameter; +import google.registry.request.RequestMethod; +import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthResult; +import google.registry.request.auth.AuthenticatedRegistrarAccessor; +import google.registry.security.XsrfTokenManager; +import google.registry.ui.server.SendEmailUtils; +import google.registry.ui.server.SoyTemplateUtils; +import google.registry.ui.soy.registrar.RegistrarCreateConsoleSoyInfo; +import google.registry.util.StringGenerator; +import java.util.HashMap; +import java.util.Optional; +import java.util.stream.Stream; +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import org.joda.money.CurrencyUnit; + +/** + * Action that serves Registrar creation page. + * + *

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. + * + *

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, + path = ConsoleRegistrarCreatorAction.PATH, + method = {Method.POST, Method.GET}, + auth = Auth.AUTH_PUBLIC) +public final class ConsoleRegistrarCreatorAction implements Runnable { + + private static final int PASSWORD_LENGTH = 16; + private static final int PASSCODE_LENGTH = 5; + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + public static final String PATH = "/registrar-create"; + + private static final Supplier TOFU_SUPPLIER = + SoyTemplateUtils.createTofuSupplier( + google.registry.ui.soy.ConsoleSoyInfo.getInstance(), + google.registry.ui.soy.FormsSoyInfo.getInstance(), + google.registry.ui.soy.registrar.RegistrarCreateConsoleSoyInfo.getInstance()); + + @VisibleForTesting // webdriver and screenshot tests need this + public static final Supplier CSS_RENAMING_MAP_SUPPLIER = + SoyTemplateUtils.createCssRenamingMapSupplier( + Resources.getResource("google/registry/ui/css/registrar_bin.css.js"), + Resources.getResource("google/registry/ui/css/registrar_dbg.css.js")); + + @Inject HttpServletRequest req; + @Inject @RequestMethod Method method; + @Inject Response response; + @Inject AuthenticatedRegistrarAccessor registrarAccessor; + @Inject UserService userService; + @Inject XsrfTokenManager xsrfTokenManager; + @Inject AuthResult authResult; + @Inject RegistryEnvironment registryEnvironment; + @Inject SendEmailUtils sendEmailUtils; + @Inject @Config("logoFilename") String logoFilename; + @Inject @Config("productName") String productName; + @Inject @Config("base58StringGenerator") StringGenerator passwordGenerator; + @Inject @Config("digitOnlyStringGenerator") StringGenerator passcodeGenerator; + @Inject @Parameter("clientId") Optional clientId; + @Inject @Parameter("name") Optional name; + @Inject @Parameter("billingAccount") Optional billingAccount; + @Inject @Parameter("ianaId") Optional ianaId; + @Inject @Parameter("referralEmail") Optional referralEmail; + @Inject @Parameter("driveId") Optional driveId; + @Inject @Parameter("email") Optional email; + + // Address fields, some of which are required and others are optional. + @Inject @Parameter("street1") Optional street1; + @Inject @Parameter("street2") Optional optionalStreet2; + @Inject @Parameter("street3") Optional optionalStreet3; + @Inject @Parameter("city") Optional city; + @Inject @Parameter("state") Optional optionalState; + @Inject @Parameter("zip") Optional optionalZip; + @Inject @Parameter("countryCode") Optional countryCode; + + @Inject @Parameter("password") Optional optionalPassword; + @Inject @Parameter("passcode") Optional optionalPasscode; + + @Inject ConsoleRegistrarCreatorAction() {} + + @Override + public void run() { + response.setHeader(X_FRAME_OPTIONS, "SAMEORIGIN"); // Disallow iframing. + response.setHeader("X-Ui-Compatible", "IE=edge"); // Ask IE not to be silly. + + logger.atInfo().log( + "User %s is accessing the Registrar creation page. Method= %s", + registrarAccessor.userIdForLogging(), method); + if (!authResult.userAuthInfo().isPresent()) { + response.setStatus(SC_MOVED_TEMPORARILY); + String location; + try { + location = userService.createLoginURL(req.getRequestURI()); + } catch (IllegalArgumentException e) { + // UserServiceImpl.createLoginURL() throws IllegalArgumentException if underlying API call + // returns an error code of NOT_ALLOWED. createLoginURL() assumes that the error is caused + // by an invalid URL. But in fact, the error can also occur if UserService doesn't have any + // user information, which happens when the request has been authenticated as internal. In + // this case, we want to avoid dying before we can send the redirect, so just redirect to + // the root path. + location = "/"; + } + response.setHeader(LOCATION, location); + return; + } + User user = authResult.userAuthInfo().get().user(); + + // Using HashMap to allow null values + HashMap data = new HashMap<>(); + data.put("logoFilename", logoFilename); + data.put("productName", productName); + data.put("username", user.getNickname()); + data.put("logoutUrl", userService.createLogoutURL(PATH)); + data.put("xsrfToken", xsrfTokenManager.generateToken(user.getEmail())); + response.setContentType(MediaType.HTML_UTF_8); + + if (!registrarAccessor.isAdmin()) { + response.setStatus(SC_FORBIDDEN); + response.setPayload( + TOFU_SUPPLIER + .get() + .newRenderer(RegistrarCreateConsoleSoyInfo.WHOAREYOU) + .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) + .setData(data) + .render()); + return; + } + switch (method) { + case POST: + runPost(data); + return; + case GET: + runGet(data); + return; + default: + return; + } + } + + private void checkPresent(Optional value, String name) { + checkState(value.isPresent(), "Missing value for %s", name); + } + + private static final Splitter LINE_SPLITTER = + Splitter.onPattern("\r?\n").trimResults().omitEmptyStrings(); + + private static final Splitter ENTRY_SPLITTER = + Splitter.on('=').trimResults().omitEmptyStrings().limit(2); + + private static ImmutableMap parseBillingAccount(String billingAccount) { + try { + return LINE_SPLITTER.splitToList(billingAccount).stream() + .map(ENTRY_SPLITTER::splitToList) + .peek( + list -> + checkState( + list.size() == 2, + "Can't parse line %s. The format should be [currency]=[account ID]", + list)) + .collect( + toImmutableMap( + list -> CurrencyUnit.of(Ascii.toUpperCase(list.get(0))), list -> list.get(1))); + } catch (Throwable e) { + throw new RuntimeException("Error parsing billing accounts - " + e.getMessage(), e); + } + } + + private void runPost(HashMap data) { + try { + + checkPresent(clientId, "clientId"); + checkPresent(name, "name"); + checkPresent(billingAccount, "billingAccount"); + checkPresent(ianaId, "ianaId"); + checkPresent(referralEmail, "referralEmail"); + checkPresent(driveId, "driveId"); + checkPresent(email, "email"); + checkPresent(street1, "street"); + checkPresent(city, "city"); + checkPresent(countryCode, "countryCode"); + + data.put("clientId", clientId.get()); + data.put("name", name.get()); + data.put("ianaId", ianaId.get()); + data.put("referralEmail", referralEmail.get()); + data.put("billingAccount", billingAccount.get()); + data.put("driveId", driveId.get()); + data.put("email", email.get()); + + data.put("street1", street1.get()); + optionalStreet2.ifPresent(street2 -> data.put("street2", street2)); + optionalStreet3.ifPresent(street3 -> data.put("street3", street3)); + data.put("city", city.get()); + optionalState.ifPresent(state -> data.put("state", state)); + optionalZip.ifPresent(zip -> data.put("zip", zip)); + data.put("countryCode", countryCode.get()); + + String gaeUserId = + checkNotNull( + convertEmailAddressToGaeUserId(email.get()), + "Email address %s is not associated with any GAE ID", + email.get()); + String password = optionalPassword.orElse(passwordGenerator.createString(PASSWORD_LENGTH)); + String phonePasscode = + optionalPasscode.orElse(passcodeGenerator.createString(PASSCODE_LENGTH)); + Registrar registrar = + new Registrar.Builder() + .setClientId(clientId.get()) + .setRegistrarName(name.get()) + .setBillingAccountMap(parseBillingAccount(billingAccount.get())) + .setIanaIdentifier(Long.valueOf(ianaId.get())) + .setIcannReferralEmail(referralEmail.get()) + .setDriveFolderId(driveId.get()) + .setType(Registrar.Type.REAL) + .setPassword(password) + .setPhonePasscode(phonePasscode) + .setState(Registrar.State.PENDING) + .setLocalizedAddress( + new RegistrarAddress.Builder() + .setStreet( + Stream.of(street1, optionalStreet2, optionalStreet3) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(toImmutableList())) + .setCity(city.get()) + .setState(optionalState.orElse(null)) + .setCountryCode(countryCode.get()) + .setZip(optionalZip.orElse(null)) + .build()) + .build(); + RegistrarContact contact = + new RegistrarContact.Builder() + .setParent(registrar) + .setName(email.get()) + .setEmailAddress(email.get()) + .setGaeUserId(gaeUserId) + .build(); + ofy() + .transact( + () -> { + checkState( + !Registrar.loadByClientId(registrar.getClientId()).isPresent(), + "Registrar with client ID %s already exists", + registrar.getClientId()); + ofy().save().entities(registrar, contact); + }); + data.put("password", password); + data.put("passcode", phonePasscode); + + sendExternalUpdates(); + response.setPayload( + TOFU_SUPPLIER + .get() + .newRenderer(RegistrarCreateConsoleSoyInfo.RESULT_SUCCESS) + .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) + .setData(data) + .render()); + } catch (Throwable e) { + logger.atWarning().withCause(e).log( + "Failed to create registrar. clientId: %s, data: %s", clientId.get(), data); + data.put("errorMessage", e.getMessage()); + response.setPayload( + TOFU_SUPPLIER + .get() + .newRenderer(RegistrarCreateConsoleSoyInfo.FORM_PAGE) + .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) + .setData(data) + .render()); + } + } + + private void runGet(HashMap data) { + // set the values to pre-fill, if given + data.put("clientId", clientId.orElse(null)); + data.put("name", name.orElse(null)); + data.put("ianaId", ianaId.orElse(null)); + data.put("referralEmail", referralEmail.orElse(null)); + data.put("driveId", driveId.orElse(null)); + data.put("email", email.orElse(null)); + + response.setPayload( + TOFU_SUPPLIER + .get() + .newRenderer(RegistrarCreateConsoleSoyInfo.FORM_PAGE) + .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) + .setData(data) + .render()); + } + + private String toEmailLine(Optional value, String name) { + return String.format(" %s: %s\n", name, value.orElse(null)); + } + private void sendExternalUpdates() { + if (!sendEmailUtils.hasRecepients()) { + return; + } + String environment = Ascii.toLowerCase(String.valueOf(registryEnvironment)); + StringBuilder builder = new StringBuilder(); + builder + .append( + String.format( + "The following registrar was created in %s by %s:\n", + environment, registrarAccessor.userIdForLogging())) + .append(toEmailLine(clientId, "clientId")) + .append(toEmailLine(name, "name")) + .append(toEmailLine(billingAccount, "billingAccount")) + .append(toEmailLine(ianaId, "ianaId")) + .append(toEmailLine(referralEmail, "referralEmail")) + .append(toEmailLine(driveId, "driveId")) + .append(String.format("Gave user %s web access to the registrar\n", email.get())); + sendEmailUtils.sendEmail( + String.format("Registrar %s created in %s", clientId.get(), environment), + builder.toString()); + } +} diff --git a/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java b/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java index f4b7d55b1..70f695184 100644 --- a/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java +++ b/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java @@ -15,6 +15,7 @@ package google.registry.ui.server.registrar; +import static google.registry.request.RequestParameters.extractOptionalIntParameter; import static google.registry.request.RequestParameters.extractOptionalParameter; import static google.registry.request.RequestParameters.extractRequiredParameter; @@ -42,6 +43,36 @@ public final class RegistrarConsoleModule { return extractRequiredParameter(req, PARAM_CLIENT_ID); } + @Provides + @Parameter("ianaId") + static Optional provideOptionalIanaId(HttpServletRequest req) { + return extractOptionalIntParameter(req, "ianaId"); + } + + @Provides + @Parameter("billingAccount") + static Optional provideOptionalBillingAccount(HttpServletRequest req) { + return extractOptionalParameter(req, "billingAccount"); + } + + @Provides + @Parameter("name") + static Optional provideOptionalName(HttpServletRequest req) { + return extractOptionalParameter(req, "name"); + } + + @Provides + @Parameter("driveId") + static Optional provideOptionalDriveId(HttpServletRequest req) { + return extractOptionalParameter(req, "driveId"); + } + + @Provides + @Parameter("referralEmail") + static Optional provideOptionalReferralEmail(HttpServletRequest req) { + return extractOptionalParameter(req, "referralEmail"); + } + @Provides @Parameter("email") static Optional provideOptionalEmail(HttpServletRequest req) { @@ -54,9 +85,57 @@ public final class RegistrarConsoleModule { return extractRequiredParameter(req, "email"); } + @Provides + @Parameter("street1") + static Optional provideOptionalStreet1(HttpServletRequest req) { + return extractOptionalParameter(req, "street1"); + } + + @Provides + @Parameter("street2") + static Optional provideOptionalStreet2(HttpServletRequest req) { + return extractOptionalParameter(req, "street2"); + } + + @Provides + @Parameter("street3") + static Optional provideOptionalStreet3(HttpServletRequest req) { + return extractOptionalParameter(req, "street3"); + } + + @Provides + @Parameter("city") + static Optional provideOptionalCity(HttpServletRequest req) { + return extractOptionalParameter(req, "city"); + } + + @Provides + @Parameter("state") + static Optional provideOptionalState(HttpServletRequest req) { + return extractOptionalParameter(req, "state"); + } + + @Provides + @Parameter("zip") + static Optional provideOptionalZip(HttpServletRequest req) { + return extractOptionalParameter(req, "zip"); + } + + @Provides + @Parameter("countryCode") + static Optional provideOptionalCountryCode(HttpServletRequest req) { + return extractOptionalParameter(req, "countryCode"); + } + @Provides @Parameter("password") static Optional provideOptionalPassword(HttpServletRequest req) { return extractOptionalParameter(req, "password"); } + + @Provides + @Parameter("passcode") + static Optional provideOptionalPasscode(HttpServletRequest req) { + return extractOptionalParameter(req, "passcode"); + } } diff --git a/java/google/registry/ui/soy/otesetup/BUILD b/java/google/registry/ui/soy/otesetup/BUILD deleted file mode 100644 index f19b6651c..000000000 --- a/java/google/registry/ui/soy/otesetup/BUILD +++ /dev/null @@ -1,21 +0,0 @@ -package( - default_visibility = ["//java/google/registry:registry_project"], -) - -licenses(["notice"]) # Apache 2.0 - -load("@io_bazel_rules_closure//closure:defs.bzl", "closure_java_template_library", "closure_js_template_library") - -closure_js_template_library( - name = "otesetup", - srcs = glob(["*.soy"]), - data = ["//java/google/registry/ui/css:registrar_raw"], - globals = "//java/google/registry/ui:globals.txt", - deps = ["//java/google/registry/ui/soy"], -) - -closure_java_template_library( - name = "soy_java_wrappers", - srcs = glob(["*.soy"]), - java_package = "google.registry.ui.soy.otesetup", -) diff --git a/java/google/registry/ui/soy/registrar/BUILD b/java/google/registry/ui/soy/registrar/BUILD index f51b1ed6a..5481d63d4 100644 --- a/java/google/registry/ui/soy/registrar/BUILD +++ b/java/google/registry/ui/soy/registrar/BUILD @@ -8,7 +8,13 @@ load("@io_bazel_rules_closure//closure:defs.bzl", "closure_java_template_library closure_js_template_library( name = "registrar", - srcs = glob(["*.soy"]), + srcs = glob( + ["*.soy"], + exclude = [ + "OteSetupConsole.soy", + "RegistrarCreateConsole.soy", + ], + ), data = ["//java/google/registry/ui/css:registrar_raw"], globals = "//java/google/registry/ui:globals.txt", deps = ["//java/google/registry/ui/soy"], diff --git a/java/google/registry/ui/soy/otesetup/Console.soy b/java/google/registry/ui/soy/registrar/OteSetupConsole.soy similarity index 98% rename from java/google/registry/ui/soy/otesetup/Console.soy rename to java/google/registry/ui/soy/registrar/OteSetupConsole.soy index 81e306aa3..457bdcd26 100644 --- a/java/google/registry/ui/soy/otesetup/Console.soy +++ b/java/google/registry/ui/soy/registrar/OteSetupConsole.soy @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -{namespace registry.soy.registrar.console} +{namespace registry.soy.registrar.otesetup} /** @@ -35,7 +35,7 @@ {call registry.soy.console.googlebar data="all" /}