Import code from internal repository to git

This commit is contained in:
Justine Tunney 2016-03-01 17:18:14 -05:00
commit 0ef0c933d2
2490 changed files with 281594 additions and 0 deletions

View file

@ -0,0 +1,131 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.ui.server.admin;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.domain.registry.config.RegistryConfig;
import com.google.domain.registry.config.RegistryEnvironment;
import com.google.domain.registry.request.HttpException.NotFoundException;
import com.google.domain.registry.security.JsonResponseHelper;
import com.google.domain.registry.security.JsonTransportServlet;
import com.google.domain.registry.ui.forms.FormFieldException;
import java.util.Map;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
/** A servlet for callbacks that manipulate resources. */
public abstract class AdminResourceServlet extends JsonTransportServlet {
private static final RegistryConfig CONFIG = RegistryEnvironment.get().config();
public static final String XSRF_SCOPE = "admin";
public AdminResourceServlet() {
super(XSRF_SCOPE, true);
}
@Override
public Map<String, Object> doJsonPost(HttpServletRequest req, Map<String, ?> params) {
String op = Optional.fromNullable((String) params.get("op")).or("read");
@SuppressWarnings("unchecked")
Map<String, ?> args = (Map<String, Object>) Optional.<Object>fromNullable(params.get("args"))
.or(ImmutableMap.of());
try {
switch (op) {
case "create":
return create(req, args);
case "update":
return update(req, args);
case "delete":
return delete(req, args);
case "read":
return read(req, args);
default:
throw new UnsupportedOperationException("Unknown operation: " + op);
}
} catch (FormFieldException e) {
return JsonResponseHelper.createFormFieldError(e.getMessage(), e.getFieldName());
}
}
@SuppressWarnings("unused")
Map<String, Object> create(HttpServletRequest req, Map<String, ?> args) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unused")
Map<String, Object> read(HttpServletRequest req, Map<String, ?> args) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unused")
Map<String, Object> update(HttpServletRequest req, Map<String, ?> args) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unused")
Map<String, Object> delete(HttpServletRequest req, Map<String, ?> args) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
protected static <T> ImmutableList<T> getParamList(Map<String, ?> map, String identifier) {
return ImmutableList.copyOf(
Optional.fromNullable((Iterable<T>) map.get(identifier)).or(ImmutableList.<T>of()));
}
/** Like checkNotNull, but throws NotFoundException if given arg is null. */
protected static <T> T checkExists(@Nullable T obj, String msg) {
if (obj == null) {
throw new NotFoundException(msg);
}
return obj;
}
protected static String getValAsString(Map<String, ?> map, String identifier) {
return (String) map.get(identifier);
}
/** Returns the last path element or null if no path separator exists. */
@VisibleForTesting
String parsePath(HttpServletRequest req) {
String uri = req.getRequestURI();
String prefix = CONFIG.getAdminServletPathPrefix() + "/";
checkArgument(
uri.startsWith(prefix),
"Request URI must start with: %s",
prefix);
return uri.substring(prefix.length());
}
/** @return the last path element or null if no path separator exists. */
@Nullable
protected String parseId(HttpServletRequest req) {
String[] pathParts = parsePath(req).split("/");
return pathParts.length < 2 ? null : pathParts[pathParts.length - 1];
}
/** Like parseId but path must contain at least one path separator. */
protected String checkParseId(HttpServletRequest req) {
return checkNotNull(parseId(req), "Path must be of the form (/<collection>)+/<id>");
}
}

View file

@ -0,0 +1,48 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.ui.server.admin;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.io.Resources;
import com.google.domain.registry.ui.server.AbstractUiServlet;
import com.google.domain.registry.ui.server.SoyTemplateUtils;
import com.google.domain.registry.ui.soy.admin.ConsoleSoyInfo;
import com.google.template.soy.shared.SoyCssRenamingMap;
import com.google.template.soy.tofu.SoyTofu;
/** UI for Registry operations. */
public class AdminUiServlet extends AbstractUiServlet {
@VisibleForTesting
static final Supplier<SoyTofu> TOFU_SUPPLIER =
SoyTemplateUtils.createTofuSupplier(
com.google.domain.registry.ui.soy.ConsoleSoyInfo.getInstance(),
com.google.domain.registry.ui.soy.admin.ConsoleSoyInfo.getInstance());
public static final Supplier<SoyCssRenamingMap> CSS_RENAMING_MAP_SUPPLIER =
SoyTemplateUtils.createCssRenamingMapSupplier(
Resources.getResource("com/google/domain/registry/ui/css/admin_bin.css.js"),
Resources.getResource("com/google/domain/registry/ui/css/admin_dbg.css.js"));
@Override
protected String get() {
return TOFU_SUPPLIER.get()
.newRenderer(ConsoleSoyInfo.MAIN)
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
.setData(getTemplateArgs(AdminResourceServlet.XSRF_SCOPE))
.render();
}
}

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//java/com/google/domain/registry:registry_project"])
java_library(
name = "admin",
srcs = glob(["*.java"]),
resources = [
"//java/com/google/domain/registry/ui/css:admin_bin.css.js",
"//java/com/google/domain/registry/ui/css:admin_dbg.css.js",
],
deps = [
"//java/com/google/common/annotations",
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/domain/registry/config",
"//java/com/google/domain/registry/export/sheet",
"//java/com/google/domain/registry/flows",
"//java/com/google/domain/registry/model",
"//java/com/google/domain/registry/request",
"//java/com/google/domain/registry/security",
"//java/com/google/domain/registry/security:servlets",
"//java/com/google/domain/registry/ui/forms",
"//java/com/google/domain/registry/ui/server",
"//java/com/google/domain/registry/ui/server/registrar",
"//java/com/google/domain/registry/ui/soy:soy_java_wrappers",
"//java/com/google/domain/registry/ui/soy/admin:soy_java_wrappers",
"//java/com/google/domain/registry/util",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//third_party/closure/templates",
],
)

View file

@ -0,0 +1,165 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.ui.server.admin;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.export.sheet.SyncRegistrarsSheetTask;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.model.registrar.RegistrarContact;
import com.google.domain.registry.ui.forms.FormFields;
import com.google.domain.registry.ui.server.RegistrarFormFields;
import com.googlecode.objectify.VoidWork;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
/**
* Admin servlet that allows creating or updating a registrar. Deletes are not allowed so as to
* preserve history.
*/
public class RegistrarServlet extends AdminResourceServlet {
@Override
public Map<String, Object> create(HttpServletRequest req, final Map<String, ?> args) {
final String clientIdentifier = FormFields.CLID.convert(parseId(req)).get();
ofy().transact(new VoidWork() {
@Override
public void vrun() {
Registrar registrar = new Registrar.Builder()
.setClientIdentifier(clientIdentifier)
.setRegistrarName(clientIdentifier)
.setType(Registrar.Type.TEST)
.setState(Registrar.State.ACTIVE)
.setAllowedTlds(ImmutableSet.<String>of())
.build();
Registrar.Builder builder = registrar.asBuilder();
Set<RegistrarContact> contacts = update(registrar, builder, args);
registrar = builder.build();
ofy().save().entity(registrar);
if (!contacts.isEmpty()) {
RegistrarContact.updateContacts(registrar, contacts);
}
}});
return ImmutableMap.<String, Object>of("results", ImmutableList.of(clientIdentifier + ": ok"));
}
@Override
public Map<String, Object> read(HttpServletRequest req, Map<String, ?> args) {
String clientIdentifier = parseId(req);
if (clientIdentifier == null) {
List<Map<String, ?>> registrars = new ArrayList<>();
for (Registrar registrar : Registrar.loadAll()) {
registrars.add(registrar.toJsonMap());
}
return ImmutableMap.<String, Object>of("set", registrars);
}
Registrar registrar = Registrar.loadByClientId(clientIdentifier);
checkExists(registrar, "No registrar exists with the given client id: " + clientIdentifier);
return ImmutableMap.<String, Object>of("item", registrar.toJsonMap());
}
@Override
public Map<String, Object> update(HttpServletRequest req, final Map<String, ?> args) {
final String clientIdentifier = checkParseId(req);
ofy().transact(new VoidWork() {
@Override
public void vrun() {
Registrar registrar = Registrar.loadByClientId(clientIdentifier);
Registrar.Builder builder = checkExists(
registrar,
"No registrar exists with the given client id: " + clientIdentifier)
.asBuilder();
Set<RegistrarContact> updatedContacts = update(registrar, builder, args);
if (!updatedContacts.isEmpty()) {
builder.setContactsRequireSyncing(true);
}
Registrar updatedRegistrar = builder.build();
ofy().save().entity(updatedRegistrar);
if (!updatedContacts.isEmpty()) {
RegistrarContact.updateContacts(updatedRegistrar, updatedContacts);
}
SyncRegistrarsSheetTask.enqueueBackendTask();
}});
return ImmutableMap.<String, Object>of("results", ImmutableList.of(clientIdentifier + ": ok"));
}
/**
* Admin fields are updated and then a chained call is made to
* {@link com.google.domain.registry.ui.server.registrar.RegistrarServlet#update(
* Registrar, Registrar.Builder, Map)}
* for the shared fields.
*/
private static Set<RegistrarContact> update(
Registrar existingRegistrarObj, Registrar.Builder builder, Map<String, ?> args) {
// Admin only settings
for (Registrar.State state :
RegistrarFormFields.STATE_FIELD.extractUntyped(args).asSet()) {
builder.setState(state);
}
builder.setAllowedTlds(
RegistrarFormFields.ALLOWED_TLDS_FIELD.extractUntyped(args).or(ImmutableSet.<String>of()));
Boolean blockPremiumNames =
RegistrarFormFields.BLOCK_PREMIUM_NAMES_FIELD.extractUntyped(args).orNull();
builder.setBlockPremiumNames(blockPremiumNames == null ? false : blockPremiumNames);
for (String password :
RegistrarFormFields.PASSWORD_FIELD.extractUntyped(args).asSet()) {
builder.setPassword(password);
}
for (Long billingIdentifier :
RegistrarFormFields.BILLING_IDENTIFIER_FIELD.extractUntyped(args).asSet()) {
builder.setBillingIdentifier(billingIdentifier);
}
// Resources
for (String driveFolderId :
RegistrarFormFields.DRIVE_FOLDER_ID_FIELD.extractUntyped(args).asSet()) {
builder.setDriveFolderId(driveFolderId);
}
// WHOIS
for (String registrarName :
RegistrarFormFields.NAME_FIELD.extractUntyped(args).asSet()) {
builder.setRegistrarName(registrarName);
}
for (Long ianaIdentifier :
RegistrarFormFields.IANA_IDENTIFIER_FIELD.extractUntyped(args).asSet()) {
builder.setIanaIdentifier(ianaIdentifier);
}
builder.setIcannReferralEmail(
RegistrarFormFields.ICANN_REFERRAL_EMAIL_FIELD.extractUntyped(args).get());
// Security
for (String phonePasscode :
RegistrarFormFields.PHONE_PASSCODE_FIELD.extractUntyped(args).asSet()) {
builder.setPhonePasscode(phonePasscode);
}
// Will this ever get used?
builder.setUrl(
RegistrarFormFields.URL_FIELD.extractUntyped(args).orNull());
return com.google.domain.registry.ui.server.registrar.RegistrarServlet.update(
existingRegistrarObj, builder, args);
}
}

View file

@ -0,0 +1,172 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.ui.server.admin;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.model.registry.Registries.getTlds;
import static com.google.domain.registry.util.DomainNameUtils.canonicalizeDomainName;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import com.google.domain.registry.model.registry.Registry;
import com.google.domain.registry.model.registry.Registry.TldState;
import com.google.domain.registry.util.SystemClock;
import com.googlecode.objectify.VoidWork;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Seconds;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
/**
* RESTful CRUD bindings for Registry objects.
*/
public class RegistryServlet extends AdminResourceServlet {
private static final SystemClock CLOCK = new SystemClock();
@Override
Map<String, Object> create(HttpServletRequest req, Map<String, ?> args) {
final String tld = checkParseId(req);
checkArgument(tld.equals(canonicalizeDomainName(tld)));
try {
ofy().transact(new VoidWork() {
@Override
public void vrun() {
ofy().save().entity(new Registry.Builder().setTldStr(tld).build());
}});
return ImmutableMap.<String, Object>of("results", ImmutableList.of(tld + ": ok"));
} catch (Exception e) {
return ImmutableMap.<String, Object>of("results", ImmutableList.of(tld + ": " + e));
}
}
@Override
Map<String, Object> read(HttpServletRequest req, Map<String, ?> args) {
String id = parseId(req);
if (id != null) {
return readTld(id);
}
// Collection request; if no item specified, return all.
return ImmutableMap.<String, Object>of("set", readTlds(getTlds()));
}
Map<String, Object> readTld(String tld) {
return ImmutableMap.<String, Object>of("item", toResultObject(
checkExists(Registry.get(tld), "No registry exists for the given tld: " + tld)));
}
List<Map<String, ?>> readTlds(Set<String> tlds) {
List<Map<String, ?>> registries = new ArrayList<>();
for (String tld : tlds) {
registries.add(toResultObject(Registry.get(tld)));
}
return registries;
}
Map<String, Object> toResultObject(Registry r) {
return new ImmutableSortedMap.Builder<String, Object>(Ordering.natural())
.put("name", r.getTld().toString())
.put("state", r.getTldState(CLOCK.nowUtc()).toString())
.put("tldStateTransitions", r.getTldStateTransitions().toString())
.put("creationTime", Objects.toString(r.getCreationTime(), ""))
.put("lastUpdateTime", Objects.toString(r.getUpdateAutoTimestamp().getTimestamp(), ""))
.put("addGracePeriod", r.getAddGracePeriodLength().toStandardSeconds().toString())
.put("autoRenewGracePeriod", r.getAutoRenewGracePeriodLength().toStandardSeconds().toString())
.put("redemptionGracePeriod", r.getRedemptionGracePeriodLength().toStandardSeconds().toString())
.put("renewGracePeriod", r.getRenewGracePeriodLength().toStandardSeconds().toString())
.put("transferGracePeriod", r.getTransferGracePeriodLength().toStandardSeconds().toString())
.put("automaticTransferLength", r.getAutomaticTransferLength().toStandardSeconds().toString())
.put("pendingDeleteLength", r.getPendingDeleteLength().toStandardSeconds().toString())
.build();
}
ImmutableSortedMap<DateTime, TldState> getTldStateTransitions(Map<String, ?> args) {
ImmutableSortedMap.Builder<DateTime, TldState> builder = ImmutableSortedMap.naturalOrder();
for (Map<String, ?> tldStateTransition
: AdminResourceServlet.<Map<String, ?>>getParamList(args, "tldStateTransitions")) {
builder.put(
DateTime.parse(getValAsString(tldStateTransition, "transitionTime")),
TldState.valueOf(getValAsString(tldStateTransition, "tldState")));
}
return builder.build();
}
Duration getValAsDuration(Map<String, ?> map, String identifier) {
return Seconds.parseSeconds(getValAsString(map, identifier)).toStandardDuration();
}
@Override
Map<String, Object> update(HttpServletRequest req, Map<String, ?> args) {
String tld = checkParseId(req);
ImmutableSortedMap<DateTime, TldState> tldStateTransitions =
getTldStateTransitions(args);
Duration addGracePeriodLength = getValAsDuration(args, "addGracePeriod");
Duration autoRenewGracePeriodLength = getValAsDuration(args, "autoRenewGracePeriod");
Duration redemptionGracePeriodLength = getValAsDuration(args, "redemptionGracePeriod");
Duration renewGracePeriodLength = getValAsDuration(args, "renewGracePeriod");
Duration transferGracePeriodLength = getValAsDuration(args, "transferGracePeriod");
Duration automaticTransferLength = getValAsDuration(args, "automaticTransferLength");
Duration pendingDeleteLength = getValAsDuration(args, "pendingDeleteLength");
try {
final Registry.Builder registry = Registry.get(tld).asBuilder();
if (!tldStateTransitions.isEmpty()) {
registry.setTldStateTransitions(tldStateTransitions);
}
if (!addGracePeriodLength.equals(Duration.ZERO)) {
registry.setAddGracePeriodLength(addGracePeriodLength);
}
if (!autoRenewGracePeriodLength.equals(Duration.ZERO)) {
registry.setAutoRenewGracePeriodLength(autoRenewGracePeriodLength);
}
if (!redemptionGracePeriodLength.equals(Duration.ZERO)) {
registry.setRedemptionGracePeriodLength(redemptionGracePeriodLength);
}
if (!renewGracePeriodLength.equals(Duration.ZERO)) {
registry.setRenewGracePeriodLength(renewGracePeriodLength);
}
if (!transferGracePeriodLength.equals(Duration.ZERO)) {
registry.setTransferGracePeriodLength(transferGracePeriodLength);
}
if (!automaticTransferLength.equals(Duration.ZERO)) {
registry.setAutomaticTransferLength(automaticTransferLength);
}
if (!pendingDeleteLength.equals(Duration.ZERO)) {
registry.setPendingDeleteLength(pendingDeleteLength);
}
ofy().transact(new VoidWork(){
@Override
public void vrun() {
ofy().save().entity(registry.build());
}});
return ImmutableMap.<String, Object>of("results", "OK");
} catch (Exception e) {
return ImmutableMap.<String, Object>of("results", e.toString());
}
}
}

View file

@ -0,0 +1,337 @@
// Copyright 2016 Google Inc. 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 com.google.domain.registry.ui.server.admin;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Maps.toMap;
import static com.google.domain.registry.flows.EppXmlTransformer.unmarshal;
import static com.google.domain.registry.flows.FlowRegistry.getFlowClass;
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
import static com.google.domain.registry.util.CollectionUtils.isNullOrEmpty;
import static com.google.domain.registry.util.DomainNameUtils.ACE_PREFIX;
import static java.util.Arrays.asList;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multiset;
import com.google.domain.registry.flows.EppException;
import com.google.domain.registry.flows.Flow;
import com.google.domain.registry.flows.contact.ContactCreateFlow;
import com.google.domain.registry.flows.contact.ContactDeleteFlow;
import com.google.domain.registry.flows.contact.ContactTransferApproveFlow;
import com.google.domain.registry.flows.contact.ContactTransferCancelFlow;
import com.google.domain.registry.flows.contact.ContactTransferRejectFlow;
import com.google.domain.registry.flows.contact.ContactTransferRequestFlow;
import com.google.domain.registry.flows.contact.ContactUpdateFlow;
import com.google.domain.registry.flows.domain.DomainApplicationCreateFlow;
import com.google.domain.registry.flows.domain.DomainApplicationDeleteFlow;
import com.google.domain.registry.flows.domain.DomainApplicationUpdateFlow;
import com.google.domain.registry.flows.domain.DomainCreateFlow;
import com.google.domain.registry.flows.domain.DomainDeleteFlow;
import com.google.domain.registry.flows.domain.DomainRenewFlow;
import com.google.domain.registry.flows.domain.DomainRestoreRequestFlow;
import com.google.domain.registry.flows.domain.DomainTransferApproveFlow;
import com.google.domain.registry.flows.domain.DomainTransferCancelFlow;
import com.google.domain.registry.flows.domain.DomainTransferRejectFlow;
import com.google.domain.registry.flows.domain.DomainTransferRequestFlow;
import com.google.domain.registry.flows.domain.DomainUpdateFlow;
import com.google.domain.registry.flows.host.HostCreateFlow;
import com.google.domain.registry.flows.host.HostDeleteFlow;
import com.google.domain.registry.flows.host.HostUpdateFlow;
import com.google.domain.registry.model.domain.DomainCommand;
import com.google.domain.registry.model.domain.fee.FeeCreateExtension;
import com.google.domain.registry.model.domain.launch.LaunchCreateExtension;
import com.google.domain.registry.model.domain.secdns.SecDnsCreateExtension;
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension;
import com.google.domain.registry.model.eppinput.EppInput;
import com.google.domain.registry.model.eppinput.EppInput.ResourceCommandWrapper;
import com.google.domain.registry.model.host.HostCommand;
import com.google.domain.registry.model.reporting.HistoryEntry;
import com.google.domain.registry.security.JsonTransportServlet;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
/**
* A servlet that verifies a registrar's OTE status. Note that this is eventually consistent, so
* OT&amp;E commands that have been run just previously to verification may not be picked up yet.
*/
public class VerifyOteServlet extends JsonTransportServlet {
public static final String XSRF_SCOPE = "admin";
public VerifyOteServlet() {
super(XSRF_SCOPE, true);
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> doJsonPost(HttpServletRequest req, Map<String, ?> params) {
final boolean summarize = Boolean.parseBoolean((String) params.get("summarize"));
return toMap(
(List<String>) params.get("registrars"),
new Function<String, Object>() {
@Nonnull
@Override
public Object apply(@Nonnull String registrar) {
return checkRegistrar(registrar, summarize);
}});
}
/** Checks whether the provided registrar has passed OT&amp;E and returns relevant information. */
private String checkRegistrar(String registrarName, boolean summarize) {
HistoryEntryStats historyEntryStats =
new HistoryEntryStats().recordRegistrarHistory(registrarName);
List<String> failureMessages = historyEntryStats.findFailures();
String passedFraction = String.format(
"%2d/%2d", StatType.NUM_REQUIREMENTS - failureMessages.size(), StatType.NUM_REQUIREMENTS);
String status = failureMessages.isEmpty() ? "PASS" : "FAIL";
return summarize
? String.format(
"Num actions: %4d - Reqs passed: %s - Overall: %s",
historyEntryStats.statCounts.size(),
passedFraction,
status)
: String.format(
"%s\n%s\nRequirements passed: %s\nOverall OT&E status: %s\n",
historyEntryStats,
Joiner.on('\n').join(failureMessages),
passedFraction,
status);
}
private static final Predicate<EppInput> HAS_CLAIMS_NOTICE = new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
LaunchCreateExtension launchCreate =
eppInput.getSingleExtension(LaunchCreateExtension.class);
return launchCreate != null && launchCreate.getNotice() != null;
}};
private static final Predicate<EppInput> HAS_FEE = new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
return eppInput.getSingleExtension(FeeCreateExtension.class) != null;
}};
private static final Predicate<EppInput> HAS_SEC_DNS = new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
return (eppInput.getSingleExtension(SecDnsCreateExtension.class) != null)
|| (eppInput.getSingleExtension(SecDnsUpdateExtension.class) != null);
}};
private static final Predicate<EppInput> IS_SUNRISE = new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
LaunchCreateExtension launchCreate =
eppInput.getSingleExtension(LaunchCreateExtension.class);
return launchCreate != null && !isNullOrEmpty(launchCreate.getSignedMarks());
}};
private static final Predicate<EppInput> IS_IDN = new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
return ((DomainCommand.Create) ((ResourceCommandWrapper)
eppInput.getCommandWrapper().getCommand()).getResourceCommand())
.getFullyQualifiedDomainName().startsWith(ACE_PREFIX);
}};
private static final Predicate<EppInput> IS_SUBORDINATE = new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
return !isNullOrEmpty(((HostCommand.Create) ((ResourceCommandWrapper)
eppInput.getCommandWrapper().getCommand()).getResourceCommand())
.getInetAddresses());
}};
private static Predicate<EppInput> isFlow(final Class<? extends Flow> flowClass) {
return new Predicate<EppInput>() {
@Override
public boolean apply(@Nonnull EppInput eppInput) {
try {
return flowClass.equals(getFlowClass(eppInput));
} catch (EppException e) {
throw new RuntimeException(e);
}
}};
}
/** Enum defining the distinct statistics (types of registrar actions) to record. */
public enum StatType {
CONTACT_CREATES(0, isFlow(ContactCreateFlow.class)),
CONTACT_DELETES(0, isFlow(ContactDeleteFlow.class)),
CONTACT_TRANSFER_APPROVES(0, isFlow(ContactTransferApproveFlow.class)),
CONTACT_TRANSFER_CANCELS(0, isFlow(ContactTransferCancelFlow.class)),
CONTACT_TRANSFER_REJECTS(0, isFlow(ContactTransferRejectFlow.class)),
CONTACT_TRANSFER_REQUESTS(0, isFlow(ContactTransferRequestFlow.class)),
CONTACT_UPDATES(0, isFlow(ContactUpdateFlow.class)),
DOMAIN_APPLICATION_CREATES(0, isFlow(DomainApplicationCreateFlow.class)),
DOMAIN_APPLICATION_CREATES_LANDRUSH(
1, isFlow(DomainApplicationCreateFlow.class), not(IS_SUNRISE)),
DOMAIN_APPLICATION_CREATES_SUNRISE(1, isFlow(DomainApplicationCreateFlow.class), IS_SUNRISE),
DOMAIN_APPLICATION_DELETES(2, isFlow(DomainApplicationDeleteFlow.class)),
DOMAIN_APPLICATION_UPDATES(2, isFlow(DomainApplicationUpdateFlow.class)),
DOMAIN_CREATES(0, isFlow(DomainCreateFlow.class)),
DOMAIN_CREATES_ASCII(1, isFlow(DomainCreateFlow.class), not(IS_IDN)),
DOMAIN_CREATES_IDN(1, isFlow(DomainCreateFlow.class), IS_IDN),
DOMAIN_CREATES_WITH_CLAIMS_NOTICE(1, isFlow(DomainCreateFlow.class), HAS_CLAIMS_NOTICE),
DOMAIN_CREATES_WITH_FEE(1, isFlow(DomainCreateFlow.class), HAS_FEE),
DOMAIN_CREATES_WITH_SEC_DNS(1, isFlow(DomainCreateFlow.class), HAS_SEC_DNS),
DOMAIN_CREATES_WITHOUT_SEC_DNS(0, isFlow(DomainCreateFlow.class), not(HAS_SEC_DNS)),
DOMAIN_DELETES(2, isFlow(DomainDeleteFlow.class)),
DOMAIN_RENEWS(0, isFlow(DomainRenewFlow.class)),
DOMAIN_RESTORES(1, isFlow(DomainRestoreRequestFlow.class)),
DOMAIN_TRANSFER_APPROVES(1, isFlow(DomainTransferApproveFlow.class)),
DOMAIN_TRANSFER_CANCELS(1, isFlow(DomainTransferCancelFlow.class)),
DOMAIN_TRANSFER_REJECTS(1, isFlow(DomainTransferRejectFlow.class)),
DOMAIN_TRANSFER_REQUESTS(1, isFlow(DomainTransferRequestFlow.class)),
DOMAIN_UPDATES(0, isFlow(DomainUpdateFlow.class)),
DOMAIN_UPDATES_WITH_SEC_DNS(1, isFlow(DomainUpdateFlow.class), HAS_SEC_DNS),
DOMAIN_UPDATES_WITHOUT_SEC_DNS(0, isFlow(DomainUpdateFlow.class), not(HAS_SEC_DNS)),
HOST_CREATES(0, isFlow(HostCreateFlow.class)),
HOST_CREATES_EXTERNAL(0, isFlow(HostCreateFlow.class), not(IS_SUBORDINATE)),
HOST_CREATES_SUBORDINATE(1, isFlow(HostCreateFlow.class), IS_SUBORDINATE),
HOST_DELETES(1, isFlow(HostDeleteFlow.class)),
HOST_UPDATES(1, isFlow(HostUpdateFlow.class)),
UNCLASSIFIED_FLOWS(0, Predicates.<EppInput>alwaysFalse());
/** The number of StatTypes with a non-zero requirement. */
private static final int NUM_REQUIREMENTS = FluentIterable.from(asList(values()))
.filter(new Predicate<StatType>() {
@Override
public boolean apply(@Nonnull StatType statType) {
return statType.requirement > 0;
}})
.size();
/** Required number of times registrars must complete this action. */
final int requirement;
/** Filters to determine if this action was performed by an EppInput. */
private Predicate<EppInput>[] filters;
@SafeVarargs
StatType(int requirement, Predicate<EppInput>... filters) {
this.requirement = requirement;
this.filters = filters;
}
/** Returns a more human-readable translation of the enum constant. */
String description() {
return this.name().replace('_', ' ').toLowerCase();
}
/** An {@link EppInput} might match multiple actions, so check if this action matches. */
boolean matches(EppInput eppInput) {
return Predicates.and(filters).apply(eppInput);
}
}
/** Class to represent stats derived from HistoryEntry objects on actions taken by registrars. */
static class HistoryEntryStats {
/** Stores counts of how many times each action type was performed. */
Multiset<StatType> statCounts = HashMultiset.create();
/**
* Records data in the passed historyEntryStats object on what actions have been performed by
* the four numbered OT&amp;E variants of the registrar name.
*/
HistoryEntryStats recordRegistrarHistory(String registrarName) {
ImmutableList.Builder<String> clientIds = new ImmutableList.Builder<>();
for (int i = 1; i <= 4; i++) {
clientIds.add(String.format("%s-%d", registrarName, i));
}
for (HistoryEntry historyEntry :
ofy().load().type(HistoryEntry.class).filter("clientId in", clientIds.build()).list()) {
try {
record(historyEntry);
} catch (EppException e) {
throw new RuntimeException(e);
}
}
return this;
}
/** Interprets the data in the provided HistoryEntry and increments counters. */
void record(HistoryEntry historyEntry) throws EppException {
byte[] xmlBytes = historyEntry.getXmlBytes();
// xmlBytes can be null on contact create and update for safe-harbor compliance.
//
// TODO(b/26161587): inspect the history entry itself to handle this properly.
if (xmlBytes == null) {
return;
}
final EppInput eppInput = unmarshal(xmlBytes);
if (!statCounts.addAll(
FluentIterable.from(EnumSet.allOf(StatType.class))
.filter(
new Predicate<StatType>() {
@Override
public boolean apply(@Nonnull StatType statType) {
return statType.matches(eppInput);
}
})
.toList())) {
statCounts.add(StatType.UNCLASSIFIED_FLOWS);
}
}
/**
* Returns a list of failure messages describing any cases where the passed stats fail to
* meet the required thresholds, or the empty list if all requirements are met.
*/
List<String> findFailures() {
List<String> messages = new ArrayList<>();
for (StatType statType : StatType.values()) {
if (statCounts.count(statType) < statType.requirement) {
messages.add(String.format(
"Failure: %s %s found.",
(statType.requirement == 1 ? "No" : "Not enough"),
statType.description()));
}
}
return messages;
}
/** Returns a string showing all possible actions and how many times each was performed. */
@Override
public String toString() {
return FluentIterable.from(EnumSet.allOf(StatType.class))
.transform(
new Function<StatType, String>() {
@Nonnull
@Override
public String apply(@Nonnull StatType statType) {
return String.format(
"%s: %d", statType.description(), statCounts.count(statType));
}
})
.append(String.format("TOTAL: %d", statCounts.size()))
.join(Joiner.on("\n"));
}
}
}

View file

@ -0,0 +1,16 @@
// Copyright 2016 Google Inc. 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.
@javax.annotation.ParametersAreNonnullByDefault
package com.google.domain.registry.ui.server.admin;