diff --git a/java/google/registry/flows/ExtensionManager.java b/java/google/registry/flows/ExtensionManager.java new file mode 100644 index 000000000..7b9a7eb07 --- /dev/null +++ b/java/google/registry/flows/ExtensionManager.java @@ -0,0 +1,163 @@ +// Copyright 2016 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.flows; + +import static com.google.common.collect.Sets.difference; +import static com.google.common.collect.Sets.intersection; +import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS; +import static google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension.getCommandExtensionUri; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import google.registry.flows.EppException.CommandUseErrorException; +import google.registry.flows.EppException.SyntaxErrorException; +import google.registry.flows.EppException.UnimplementedExtensionException; +import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.exceptions.OnlyToolCanPassMetadataException; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppinput.EppInput; +import google.registry.model.eppinput.EppInput.CommandExtension; +import google.registry.util.FormattingLogger; +import java.util.Collection; +import java.util.Set; +import javax.inject.Inject; + +/** + * Helper to validate extensions on an EPP command. + * + *

This class checks that the declared extension URIs in an EPP request as well as the actually + * supplied extensions in the XML are compatible with the extensions supported by a flow. + */ +public final class ExtensionManager { + + private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); + + /** Blacklist of extension URIs that cause an error if they are used without being declared. */ + private static final ImmutableSet UNDECLARED_URIS_BLACKLIST = FEE_EXTENSION_URIS; + + private final ImmutableSet.Builder> implementedBuilder = + new ImmutableSet.Builder<>(); + private final ImmutableSet.Builder> implementedGroupsBuilder = + new ImmutableSet.Builder<>(); + + @Inject EppInput eppInput; + @Inject SessionMetadata sessionMetadata; + @Inject @ClientId String clientId; + @Inject Class flowClass; + @Inject EppRequestSource eppRequestSource; + @Inject ExtensionManager() {} + + @SafeVarargs + public final void register(Class... extension) { + implementedBuilder.add(extension); + } + + public void registerAsGroup( + Collection> extensions) { + implementedBuilder.addAll(extensions); + implementedGroupsBuilder.add(ImmutableSet.copyOf(extensions)); + } + + public void validate() throws EppException { + ImmutableSet.Builder> suppliedBuilder = + new ImmutableSet.Builder<>(); + for (CommandExtension extension : eppInput.getCommandWrapper().getExtensions()) { + suppliedBuilder.add(extension.getClass()); + } + ImmutableSet> suppliedExtensions = suppliedBuilder.build(); + ImmutableSet> implementedExtensions = + implementedBuilder.build(); + ImmutableSet> implementedExtensionGroups = + implementedGroupsBuilder.build(); + checkForDuplicateExtensions(suppliedExtensions, implementedExtensionGroups); + checkForUndeclaredExtensions(suppliedExtensions); + checkForRestrictedExtensions(suppliedExtensions); + checkForUnimplementedExtensions(suppliedExtensions, implementedExtensions); + } + + private void checkForDuplicateExtensions( + ImmutableSet> suppliedExtensions, + ImmutableSet> implementedExtensionGroups) + throws UnsupportedRepeatedExtensionException { + if (suppliedExtensions.size() < eppInput.getCommandWrapper().getExtensions().size()) { + throw new UnsupportedRepeatedExtensionException(); + } + // No more than one extension in an extension group can be present. + for (ImmutableSet group : implementedExtensionGroups) { + if (intersection(suppliedExtensions, group).size() > 1) { + throw new UnsupportedRepeatedExtensionException(); + } + } + } + + private void checkForUndeclaredExtensions( + ImmutableSet> suppliedExtensions) + throws UndeclaredServiceExtensionException { + ImmutableSet.Builder suppliedUris = new ImmutableSet.Builder<>(); + for (Class extension : suppliedExtensions) { + suppliedUris.add(getCommandExtensionUri(extension)); + } + Set declaredUris = sessionMetadata.getServiceExtensionUris(); + Set undeclaredUris = difference(suppliedUris.build(), declaredUris); + if (undeclaredUris.isEmpty()) { + return; + } + Set undeclaredUrisThatError = intersection(undeclaredUris, UNDECLARED_URIS_BLACKLIST); + if (!undeclaredUrisThatError.isEmpty()) { + throw new UndeclaredServiceExtensionException(undeclaredUrisThatError); + } + logger.infofmt( + "Client %s is attempting to run %s without declaring URIs %s on login", + clientId, + flowClass.getSimpleName(), + undeclaredUris); + } + + private void checkForRestrictedExtensions( + ImmutableSet> suppliedExtensions) + throws OnlyToolCanPassMetadataException { + if (suppliedExtensions.contains(MetadataExtension.class) + && !eppRequestSource.equals(EppRequestSource.TOOL)) { + throw new OnlyToolCanPassMetadataException(); + } + } + + private void checkForUnimplementedExtensions( + ImmutableSet> suppliedExtensions, + ImmutableSet> implementedExtensions) + throws UnimplementedExtensionException { + Set> unimplementedExtensions = + difference(suppliedExtensions, implementedExtensions); + if (!unimplementedExtensions.isEmpty()) { + logger.infofmt("Unimplemented extensions: %s", unimplementedExtensions); + throw new UnimplementedExtensionException(); + } + } + + /** Unsupported repetition of an extension. */ + static class UnsupportedRepeatedExtensionException extends SyntaxErrorException { + public UnsupportedRepeatedExtensionException() { + super("Unsupported repetition of an extension"); + } + } + + /** Service extension(s) must be declared at login. */ + public static class UndeclaredServiceExtensionException extends CommandUseErrorException { + public UndeclaredServiceExtensionException(Set undeclaredUris) { + super(String.format("Service extension(s) must be declared at login: %s", + Joiner.on(", ").join(undeclaredUris))); + } + } +} diff --git a/java/google/registry/flows/Flow.java b/java/google/registry/flows/Flow.java index 5f1ffd59b..154b32ff8 100644 --- a/java/google/registry/flows/Flow.java +++ b/java/google/registry/flows/Flow.java @@ -15,20 +15,14 @@ package google.registry.flows; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import google.registry.model.eppcommon.Trid; import google.registry.model.eppinput.EppInput; -import google.registry.model.eppinput.EppInput.CommandExtension; import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse.ResponseData; import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.eppoutput.Result; import google.registry.model.poll.MessageQueueInfo; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import javax.annotation.Nullable; import org.joda.time.DateTime; @@ -49,9 +43,6 @@ public abstract class Flow { /** Whether this flow is being run in a superuser mode that can skip some checks. */ protected boolean isSuperuser; - /** The collection of allowed extensions for the flow. */ - private Set> validExtensions = new HashSet<>(); - /** Flows can override this for custom initialization. */ @SuppressWarnings("unused") protected void initFlow() throws EppException {} @@ -107,27 +98,6 @@ public abstract class Flow { this.now = now; this.isSuperuser = isSuperuser; initFlow(); - validExtensions = ImmutableSet.copyOf(validExtensions); return this; } - - /** - * Add an extension class as a valid extension for a flow. - * Must be called in the init series of methods, as the validExtensions - * becomes immutable once init is complete. - */ - @SafeVarargs - protected final void registerExtensions(Class... extensions) { - Collections.addAll(validExtensions, extensions); - } - - protected final - void registerExtensions(List> extensions) { - validExtensions.addAll(extensions); - } - - /** Get the legal command extension types for this flow. */ - protected final Set> getValidRequestExtensions() { - return ImmutableSet.copyOf(validExtensions); - } } diff --git a/java/google/registry/flows/FlowModule.java b/java/google/registry/flows/FlowModule.java index 4202f6ede..bfe92367d 100644 --- a/java/google/registry/flows/FlowModule.java +++ b/java/google/registry/flows/FlowModule.java @@ -21,7 +21,6 @@ import com.google.common.base.Optional; import com.google.common.base.Strings; import dagger.Module; import dagger.Provides; -import google.registry.flows.exceptions.OnlyToolCanPassMetadataException; import google.registry.flows.picker.FlowPicker; import google.registry.model.domain.launch.ApplicationIdTargetExtension; import google.registry.model.domain.metadata.MetadataExtension; @@ -230,7 +229,6 @@ public class FlowModule { @InputXml byte[] inputXmlBytes, @Superuser boolean isSuperuser, @ClientId String clientId, - EppRequestSource eppRequestSource, EppInput eppInput) { HistoryEntry.Builder historyBuilder = new HistoryEntry.Builder() .setTrid(trid) @@ -239,9 +237,6 @@ public class FlowModule { .setClientId(clientId); MetadataExtension metadataExtension = eppInput.getSingleExtension(MetadataExtension.class); if (metadataExtension != null) { - if (!eppRequestSource.equals(EppRequestSource.TOOL)) { - throw new EppExceptionInProviderException(new OnlyToolCanPassMetadataException()); - } historyBuilder .setReason(metadataExtension.getReason()) .setRequestedByRegistrar(metadataExtension.getRequestedByRegistrar()); diff --git a/java/google/registry/flows/FlowUtils.java b/java/google/registry/flows/FlowUtils.java new file mode 100644 index 000000000..c21d926c6 --- /dev/null +++ b/java/google/registry/flows/FlowUtils.java @@ -0,0 +1,37 @@ +// Copyright 2016 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.flows; + +import google.registry.flows.EppException.CommandUseErrorException; + +/** Static utility functions for flows. */ +public final class FlowUtils { + + private FlowUtils() {} + + /** Validate that there is a logged in client. */ + public static void validateClientIsLoggedIn(String clientId) throws EppException { + if (clientId.isEmpty()) { + throw new NotLoggedInException(); + } + } + + /** Registrar is not logged in. */ + public static class NotLoggedInException extends CommandUseErrorException { + public NotLoggedInException() { + super("Registrar is not logged in."); + } + } +} diff --git a/java/google/registry/flows/HttpSessionMetadata.java b/java/google/registry/flows/HttpSessionMetadata.java index eb85dc1b0..7e540be68 100644 --- a/java/google/registry/flows/HttpSessionMetadata.java +++ b/java/google/registry/flows/HttpSessionMetadata.java @@ -48,7 +48,7 @@ public class HttpSessionMetadata implements SessionMetadata { @Override @SuppressWarnings("unchecked") public Set getServiceExtensionUris() { - return (Set) session.getAttribute(SERVICE_EXTENSIONS); + return nullToEmpty((Set) session.getAttribute(SERVICE_EXTENSIONS)); } @Override diff --git a/java/google/registry/flows/LoggedInFlow.java b/java/google/registry/flows/LoggedInFlow.java deleted file mode 100644 index db241f08d..000000000 --- a/java/google/registry/flows/LoggedInFlow.java +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2016 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.flows; - -import static com.google.common.base.Verify.verifyNotNull; -import static com.google.common.collect.Sets.difference; -import static com.google.common.collect.Sets.intersection; -import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS; -import static google.registry.model.registry.Registries.getTlds; -import static google.registry.util.CollectionUtils.nullToEmpty; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableSet; -import google.registry.flows.EppException.CommandUseErrorException; -import google.registry.flows.EppException.SyntaxErrorException; -import google.registry.flows.EppException.UnimplementedExtensionException; -import google.registry.flows.FlowModule.ClientId; -import google.registry.model.eppcommon.ProtocolDefinition; -import google.registry.model.eppinput.EppInput.CommandExtension; -import google.registry.model.registrar.Registrar; -import google.registry.util.FormattingLogger; -import java.util.Set; -import javax.inject.Inject; - -/** A flow that requires being logged in. */ -public abstract class LoggedInFlow extends Flow { - - static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); - - /** - * A blacklist of service extension URIs that will cause an error if they are used without being - * declared on login. - */ - private static final ImmutableSet UNDECLARED_URIS_BLACKLIST = FEE_EXTENSION_URIS; - - /** - * The TLDs on which the logged-in registrar is allowed access domains. - */ - private ImmutableSet allowedTlds; - - @Inject @ClientId String clientId; - - protected ImmutableSet getAllowedTlds() { - return allowedTlds; - } - - @Override - public final void initFlow() throws EppException { - if (clientId.isEmpty()) { - throw new NotLoggedInException(); - } - // Validate that the extensions in the input match what this flow expects. - ImmutableSet> extensionClasses = FluentIterable - .from(eppInput.getCommandWrapper().getExtensions()) - .transform(new Function>() { - @Override - public Class apply(CommandExtension extension) { - return extension.getClass(); - }}) - .toSet(); - if (extensionClasses.size() != eppInput.getCommandWrapper().getExtensions().size()) { - throw new UnsupportedRepeatedExtensionException(); - } - // Validate that we did not receive any undeclared extensions. - ImmutableSet extensionUris = FluentIterable - .from(extensionClasses) - .transform(new Function, String>() { - @Override - public String apply(Class clazz) { - return ProtocolDefinition.ServiceExtension.getCommandExtensionUri(clazz); - }}) - .toSet(); - Set undeclaredUris = difference( - extensionUris, nullToEmpty(sessionMetadata.getServiceExtensionUris())); - if (!undeclaredUris.isEmpty()) { - Set undeclaredUrisThatError = intersection(undeclaredUris, UNDECLARED_URIS_BLACKLIST); - if (!undeclaredUrisThatError.isEmpty()) { - throw new UndeclaredServiceExtensionException(undeclaredUrisThatError); - } else { - logger.infofmt( - "Client (%s) is attempting to run flow (%s) without declaring URIs %s on login", - clientId, getClass().getSimpleName(), undeclaredUris); - } - } - if (isSuperuser) { - allowedTlds = getTlds(); - } else { - Registrar registrar = verifyNotNull( - Registrar.loadByClientId(clientId), - "Could not load registrar %s", clientId); - allowedTlds = registrar.getAllowedTlds(); - } - initLoggedInFlow(); - Set> unimplementedExtensions = - difference(extensionClasses, getValidRequestExtensions()); - if (!unimplementedExtensions.isEmpty()) { - logger.infofmt("Unimplemented extensions: %s", unimplementedExtensions); - throw new UnimplementedExtensionException(); - } - } - - @SuppressWarnings("unused") - protected void initLoggedInFlow() throws EppException {} - - /** Registrar is not logged in. */ - public static class NotLoggedInException extends CommandUseErrorException { - public NotLoggedInException() { - super("Registrar is not logged in."); - } - } - - /** Unsupported repetition of an extension. */ - static class UnsupportedRepeatedExtensionException extends SyntaxErrorException { - public UnsupportedRepeatedExtensionException() { - super("Unsupported repetition of an extension"); - } - } - - /** Service extension(s) must be declared at login. */ - public static class UndeclaredServiceExtensionException extends CommandUseErrorException { - public UndeclaredServiceExtensionException(Set undeclaredUris) { - super(String.format("Service extension(s) must be declared at login: %s", - Joiner.on(", ").join(undeclaredUris))); - } - } -} diff --git a/java/google/registry/flows/ResourceFlowUtils.java b/java/google/registry/flows/ResourceFlowUtils.java index 4ae804c19..a1b7dfd97 100644 --- a/java/google/registry/flows/ResourceFlowUtils.java +++ b/java/google/registry/flows/ResourceFlowUtils.java @@ -65,8 +65,10 @@ import java.util.List; import java.util.Set; import org.joda.time.DateTime; -/** Static utility functions for resource transfer flows. */ -public class ResourceFlowUtils { +/** Static utility functions for resource flows. */ +public final class ResourceFlowUtils { + + private ResourceFlowUtils() {} /** Statuses for which an exDate should be added to transfer responses. */ private static final ImmutableSet ADD_EXDATE_STATUSES = Sets.immutableEnumSet( diff --git a/java/google/registry/flows/contact/ContactCheckFlow.java b/java/google/registry/flows/contact/ContactCheckFlow.java index 571357c4e..4897837db 100644 --- a/java/google/registry/flows/contact/ContactCheckFlow.java +++ b/java/google/registry/flows/contact/ContactCheckFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.eppoutput.Result.Code.SUCCESS; @@ -21,7 +22,9 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.collect.ImmutableList; import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; -import google.registry.flows.LoggedInFlow; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; +import google.registry.flows.FlowModule.ClientId; import google.registry.model.contact.ContactCommand.Check; import google.registry.model.contact.ContactResource; import google.registry.model.eppinput.ResourceCommand; @@ -39,14 +42,18 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} */ -public final class ContactCheckFlow extends LoggedInFlow { +public final class ContactCheckFlow extends Flow { @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; + @Inject ExtensionManager extensionManager; @Inject @Config("maxChecks") int maxChecks; @Inject ContactCheckFlow() {} @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); Set existingIds = checkResourcesExist(ContactResource.class, targetIds, now); diff --git a/java/google/registry/flows/contact/ContactCreateFlow.java b/java/google/registry/flows/contact/ContactCreateFlow.java index 9d7a7109c..14d8c8774 100644 --- a/java/google/registry/flows/contact/ContactCreateFlow.java +++ b/java/google/registry/flows/contact/ContactCreateFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; @@ -23,9 +24,10 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.contact.ContactCommand.Create; import google.registry.model.contact.ContactResource; @@ -47,21 +49,20 @@ import javax.inject.Inject; * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} */ -public final class ContactCreateFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactCreateFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactCreateFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override protected final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Create command = (Create) resourceCommand; verifyResourceDoesNotExist(ContactResource.class, targetId, now); Builder builder = new Builder(); diff --git a/java/google/registry/flows/contact/ContactDeleteFlow.java b/java/google/registry/flows/contact/ContactDeleteFlow.java index 9ed280d3c..0e057f29b 100644 --- a/java/google/registry/flows/contact/ContactDeleteFlow.java +++ b/java/google/registry/flows/contact/ContactDeleteFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; @@ -27,9 +28,10 @@ import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowEnqueuer; import google.registry.model.contact.ContactResource; @@ -55,7 +57,7 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} * @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} */ -public final class ContactDeleteFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactDeleteFlow extends Flow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.LINKED, @@ -70,20 +72,19 @@ public final class ContactDeleteFlow extends LoggedInFlow implements Transaction return domain.getReferencedContacts(); }}; - @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject Optional authInfo; @Inject HistoryEntry.Builder historyBuilder; + @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; @Inject ContactDeleteFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); failfastForAsyncDelete(targetId, now, ContactResource.class, GET_REFERENCED_CONTACTS); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyNoDisallowedStatuses(existingContact, DISALLOWED_STATUSES); diff --git a/java/google/registry/flows/contact/ContactInfoFlow.java b/java/google/registry/flows/contact/ContactInfoFlow.java index a524d2f01..efc06b677 100644 --- a/java/google/registry/flows/contact/ContactInfoFlow.java +++ b/java/google/registry/flows/contact/ContactInfoFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; @@ -21,9 +22,10 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppoutput.EppOutput; @@ -39,8 +41,9 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} */ -public final class ContactInfoFlow extends LoggedInFlow { +public final class ContactInfoFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject Optional authInfo; @@ -48,6 +51,8 @@ public final class ContactInfoFlow extends LoggedInFlow { @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, contact); if (!clientId.equals(contact.getCurrentSponsorClientId()) && !authInfo.isPresent()) { diff --git a/java/google/registry/flows/contact/ContactTransferApproveFlow.java b/java/google/registry/flows/contact/ContactTransferApproveFlow.java index 57c73a09e..184a2b5bd 100644 --- a/java/google/registry/flows/contact/ContactTransferApproveFlow.java +++ b/java/google/registry/flows/contact/ContactTransferApproveFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.approvePendingTransfer; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -26,9 +27,10 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.NotPendingTransferException; import google.registry.model.contact.ContactResource; @@ -55,26 +57,25 @@ import javax.inject.Inject; * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException} */ -public final class ContactTransferApproveFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactTransferApproveFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject Optional authInfo; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferApproveFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - /** *

The logic in this flow, which handles client approvals, very closely parallels the logic in * {@link ContactResource#cloneProjectedAtTime} which handles implicit server approvals. */ @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); TransferData transferData = existingContact.getTransferData(); diff --git a/java/google/registry/flows/contact/ContactTransferCancelFlow.java b/java/google/registry/flows/contact/ContactTransferCancelFlow.java index 46b3ee22e..b7413c3da 100644 --- a/java/google/registry/flows/contact/ContactTransferCancelFlow.java +++ b/java/google/registry/flows/contact/ContactTransferCancelFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.denyPendingTransfer; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -25,9 +26,10 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.NotPendingTransferException; import google.registry.flows.exceptions.NotTransferInitiatorException; @@ -55,22 +57,21 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link google.registry.flows.exceptions.NotTransferInitiatorException} */ -public final class ContactTransferCancelFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactTransferCancelFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferCancelFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override protected final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); TransferData transferData = existingContact.getTransferData(); diff --git a/java/google/registry/flows/contact/ContactTransferQueryFlow.java b/java/google/registry/flows/contact/ContactTransferQueryFlow.java index e7a9d2054..30c6c1828 100644 --- a/java/google/registry/flows/contact/ContactTransferQueryFlow.java +++ b/java/google/registry/flows/contact/ContactTransferQueryFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; @@ -21,9 +22,10 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.exceptions.NoTransferHistoryToQueryException; import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; import google.registry.model.contact.ContactResource; @@ -46,8 +48,9 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException} * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} */ -public final class ContactTransferQueryFlow extends LoggedInFlow { +public final class ContactTransferQueryFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -55,6 +58,8 @@ public final class ContactTransferQueryFlow extends LoggedInFlow { @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, contact); // Most of the fields on the transfer response are required, so there's no way to return valid diff --git a/java/google/registry/flows/contact/ContactTransferRejectFlow.java b/java/google/registry/flows/contact/ContactTransferRejectFlow.java index fedcc3e09..3ca325099 100644 --- a/java/google/registry/flows/contact/ContactTransferRejectFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRejectFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.denyPendingTransfer; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -26,9 +27,10 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.NotPendingTransferException; import google.registry.model.contact.ContactResource; @@ -54,21 +56,20 @@ import javax.inject.Inject; * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException} */ -public final class ContactTransferRejectFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactTransferRejectFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferRejectFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override protected final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); TransferData transferData = existingContact.getTransferData(); diff --git a/java/google/registry/flows/contact/ContactTransferRequestFlow.java b/java/google/registry/flows/contact/ContactTransferRequestFlow.java index d5cad7021..53b2bac50 100644 --- a/java/google/registry/flows/contact/ContactTransferRequestFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRequestFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyRequiredAuthInfoForResourceTransfer; @@ -28,9 +29,10 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AlreadyPendingTransferException; import google.registry.flows.exceptions.ObjectAlreadySponsoredException; @@ -62,13 +64,14 @@ import org.joda.time.Duration; * @error {@link google.registry.flows.exceptions.MissingTransferRequestAuthInfoException} * @error {@link google.registry.flows.exceptions.ObjectAlreadySponsoredException} */ -public final class ContactTransferRequestFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactTransferRequestFlow extends Flow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.CLIENT_TRANSFER_PROHIBITED, StatusValue.PENDING_DELETE, StatusValue.SERVER_TRANSFER_PROHIBITED); + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String gainingClientId; @Inject @TargetId String targetId; @@ -76,13 +79,11 @@ public final class ContactTransferRequestFlow extends LoggedInFlow implements Tr @Inject HistoryEntry.Builder historyBuilder; @Inject ContactTransferRequestFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override protected final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(gainingClientId); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyRequiredAuthInfoForResourceTransfer(authInfo, existingContact); // Verify that the resource does not already have a pending transfer. diff --git a/java/google/registry/flows/contact/ContactUpdateFlow.java b/java/google/registry/flows/contact/ContactUpdateFlow.java index 52e915dcc..46ac20e41 100644 --- a/java/google/registry/flows/contact/ContactUpdateFlow.java +++ b/java/google/registry/flows/contact/ContactUpdateFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.contact; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -28,9 +29,10 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AddRemoveSameValueEppException; import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; @@ -59,7 +61,7 @@ import javax.inject.Inject; * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} */ -public final class ContactUpdateFlow extends LoggedInFlow implements TransactionalFlow { +public final class ContactUpdateFlow extends Flow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -71,19 +73,18 @@ public final class ContactUpdateFlow extends LoggedInFlow implements Transaction StatusValue.SERVER_UPDATE_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject ContactUpdateFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Update command = (Update) resourceCommand; ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); diff --git a/java/google/registry/flows/domain/ClaimsCheckFlow.java b/java/google/registry/flows/domain/ClaimsCheckFlow.java index 66aea6fbe..cd02db10b 100644 --- a/java/google/registry/flows/domain/ClaimsCheckFlow.java +++ b/java/google/registry/flows/domain/ClaimsCheckFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.validateDomainName; @@ -29,7 +30,9 @@ import com.google.common.net.InternetDomainName; import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; -import google.registry.flows.LoggedInFlow; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; +import google.registry.flows.FlowModule.ClientId; import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.launch.LaunchCheckExtension; import google.registry.model.domain.launch.LaunchCheckResponseExtension; @@ -55,19 +58,19 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link ClaimsCheckNotAllowedInSunrise} */ -public final class ClaimsCheckFlow extends LoggedInFlow { +public final class ClaimsCheckFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; @Inject @Config("maxChecks") int maxChecks; @Inject ClaimsCheckFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(LaunchCheckExtension.class); - } - @Override public EppOutput run() throws EppException { + extensionManager.register(LaunchCheckExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); Set seenTlds = new HashSet<>(); @@ -78,7 +81,7 @@ public final class ClaimsCheckFlow extends LoggedInFlow { String tld = domainName.parent().toString(); // Only validate access to a TLD the first time it is encountered. if (seenTlds.add(tld)) { - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); Registry registry = Registry.get(tld); if (!isSuperuser) { verifyNotInPredelegation(registry, now); diff --git a/java/google/registry/flows/domain/DomainAllocateFlow.java b/java/google/registry/flows/domain/DomainAllocateFlow.java index 497ad03fe..b7647ab87 100644 --- a/java/google/registry/flows/domain/DomainAllocateFlow.java +++ b/java/google/registry/flows/domain/DomainAllocateFlow.java @@ -16,6 +16,7 @@ package google.registry.flows.domain; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.getOnlyElement; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse; @@ -46,9 +47,10 @@ import google.registry.flows.EppException; import google.registry.flows.EppException.AuthorizationErrorException; import google.registry.flows.EppException.ObjectDoesNotExistException; import google.registry.flows.EppException.StatusProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; import google.registry.model.ImmutableObject; @@ -95,13 +97,14 @@ import org.joda.time.DateTime; * @error {@link DomainAllocateFlow.MissingApplicationException} * @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException} */ -public class DomainAllocateFlow extends LoggedInFlow implements TransactionalFlow { +public class DomainAllocateFlow extends Flow implements TransactionalFlow { private static final String COLLISION_MESSAGE = "Domain on the name collision list was allocated. But by policy, the domain will not be " + "delegated. Please visit https://www.icann.org/namecollision for more information on name " + "collision."; + @Inject ExtensionManager extensionManager; @Inject AuthInfo authInfo; @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @@ -110,17 +113,15 @@ public class DomainAllocateFlow extends LoggedInFlow implements TransactionalFlo @Inject DomainAllocateFlow() {} @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - registerExtensions( + public final EppOutput run() throws EppException { + extensionManager.register( SecDnsCreateExtension.class, FlagsCreateCommandExtension.class, MetadataExtension.class, AllocateCreateExtension.class); - } - - @Override - public final EppOutput run() throws EppException { + extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); verifyIsSuperuser(); Create command = cloneAndLinkReferences((Create) resourceCommand, now); failfastForCreate(targetId, now); diff --git a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java index 4e68a2851..53ae5d420 100644 --- a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java @@ -15,6 +15,7 @@ package google.registry.flows.domain; import static com.google.common.collect.Iterables.getOnlyElement; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; @@ -54,9 +55,10 @@ import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.ObjectAlreadyExistsException; import google.registry.flows.EppException.RequiredParameterMissingException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; import google.registry.model.ImmutableObject; @@ -151,8 +153,9 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} * @error {@link DomainFlowUtils.UnsupportedMarkTypeException} */ -public final class DomainApplicationCreateFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainApplicationCreateFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject AuthInfo authInfo; @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @@ -161,17 +164,15 @@ public final class DomainApplicationCreateFlow extends LoggedInFlow implements T @Inject DomainApplicationCreateFlow() {} @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - registerExtensions( + public final EppOutput run() throws EppException { + extensionManager.register( SecDnsCreateExtension.class, FlagsCreateCommandExtension.class, MetadataExtension.class, LaunchCreateExtension.class); - } - - @Override - public final EppOutput run() throws EppException { + extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Create command = cloneAndLinkReferences((Create) resourceCommand, now); failfastForCreate(targetId, now); // Fail if the domain is already registered (e.g. this is a landrush application but the domain @@ -182,7 +183,7 @@ public final class DomainApplicationCreateFlow extends LoggedInFlow implements T InternetDomainName domainName = validateDomainName(targetId); String idnTableName = validateDomainNameWithIdnTables(domainName); String tld = domainName.parent().toString(); - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); Registry registry = Registry.get(tld); EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice( registry, targetId, clientId, now, command.getPeriod().getValue(), eppInput); diff --git a/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java b/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java index 3a4edad5a..6bb5949ef 100644 --- a/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.handlePendingTransferOnDelete; import static google.registry.flows.ResourceFlowUtils.prepareDeletedResourceAsBuilder; import static google.registry.flows.ResourceFlowUtils.updateForeignKeyIndexDeletionTime; @@ -32,10 +33,11 @@ import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.EppException.StatusProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.launch.LaunchDeleteExtension; @@ -60,8 +62,9 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.LaunchPhaseMismatchException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainApplicationDeleteFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainApplicationDeleteFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -69,20 +72,17 @@ public final class DomainApplicationDeleteFlow extends LoggedInFlow implements T @Inject HistoryEntry.Builder historyBuilder; @Inject DomainApplicationDeleteFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - registerExtensions(LaunchDeleteExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class, LaunchDeleteExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); DomainApplication existingApplication = verifyExistence( DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); verifyApplicationDomainMatchesTargetId(existingApplication, targetId); verifyOptionalAuthInfoForResource(authInfo, existingApplication); String tld = existingApplication.getTld(); - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); if (!isSuperuser) { Registry registry = Registry.get(tld); verifyRegistryStateAllowsLaunchFlows(registry, now); diff --git a/java/google/registry/flows/domain/DomainApplicationInfoFlow.java b/java/google/registry/flows/domain/DomainApplicationInfoFlow.java index 2d7449ffb..79601f601 100644 --- a/java/google/registry/flows/domain/DomainApplicationInfoFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationInfoFlow.java @@ -15,6 +15,7 @@ package google.registry.flows.domain; import static google.registry.flows.EppXmlTransformer.unmarshal; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; @@ -28,10 +29,11 @@ import com.google.common.collect.ImmutableList; import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainCommand.Info; import google.registry.model.domain.launch.LaunchInfoExtension; @@ -57,22 +59,21 @@ import javax.inject.Inject; * @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException} * @error {@link MissingApplicationIdException} */ -public final class DomainApplicationInfoFlow extends LoggedInFlow { +public final class DomainApplicationInfoFlow extends Flow { @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject @ApplicationId String applicationId; @Inject DomainApplicationInfoFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(LaunchInfoExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(LaunchInfoExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); if (applicationId.isEmpty()) { throw new MissingApplicationIdException(); } diff --git a/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java b/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java index ddbc12d4b..b42061681 100644 --- a/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java @@ -16,6 +16,7 @@ package google.registry.flows.domain; import static com.google.common.base.CaseFormat.LOWER_CAMEL; import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -45,10 +46,11 @@ import com.google.common.collect.Sets; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.EppException.StatusProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AddRemoveSameValueEppException; import google.registry.model.ImmutableObject; @@ -97,7 +99,7 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException} * @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException} */ -public class DomainApplicationUpdateFlow extends LoggedInFlow implements TransactionalFlow { +public class DomainApplicationUpdateFlow extends Flow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -116,6 +118,7 @@ public class DomainApplicationUpdateFlow extends LoggedInFlow implements Transac ApplicationStatus.ALLOCATED); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -123,15 +126,15 @@ public class DomainApplicationUpdateFlow extends LoggedInFlow implements Transac @Inject HistoryEntry.Builder historyBuilder; @Inject DomainApplicationUpdateFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - registerExtensions( - MetadataExtension.class, LaunchUpdateExtension.class, SecDnsUpdateExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register( + LaunchUpdateExtension.class, + MetadataExtension.class, + SecDnsUpdateExtension.class); + extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Update command = cloneAndLinkReferences((Update) resourceCommand, now); DomainApplication existingApplication = verifyExistence( DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); @@ -154,7 +157,7 @@ public class DomainApplicationUpdateFlow extends LoggedInFlow implements Transac verifyStatusChangesAreClientSettable(command); } String tld = existingApplication.getTld(); - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); if (UPDATE_DISALLOWED_APPLICATION_STATUSES .contains(existingApplication.getApplicationStatus())) { throw new ApplicationStatusProhibitsUpdateException( diff --git a/java/google/registry/flows/domain/DomainCheckFlow.java b/java/google/registry/flows/domain/DomainCheckFlow.java index ac4daddb1..d8909eb7a 100644 --- a/java/google/registry/flows/domain/DomainCheckFlow.java +++ b/java/google/registry/flows/domain/DomainCheckFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.getReservationType; @@ -40,8 +41,9 @@ import com.google.common.net.InternetDomainName; import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValuePolicyErrorException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; -import google.registry.flows.LoggedInFlow; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.DomainResource; @@ -88,7 +90,7 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.UnknownFeeCommandException} * @error {@link OnlyCheckedNamesCanBeFeeCheckedException} */ -public final class DomainCheckFlow extends LoggedInFlow { +public final class DomainCheckFlow extends Flow { /** * The TLD states during which we want to report a domain with pending applications as @@ -98,18 +100,17 @@ public final class DomainCheckFlow extends LoggedInFlow { Sets.immutableEnumSet(TldState.GENERAL_AVAILABILITY, TldState.QUIET_PERIOD); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @Config("maxChecks") int maxChecks; @Inject DomainCheckFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(LaunchCheckExtension.class); - registerExtensions(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - } - @Override public EppOutput run() throws EppException { + extensionManager.register(LaunchCheckExtension.class); + extensionManager.registerAsGroup(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); ImmutableMap.Builder domains = new ImmutableMap.Builder<>(); @@ -122,7 +123,7 @@ public final class DomainCheckFlow extends LoggedInFlow { domains.put(targetId, domainName); String tld = domainName.parent().toString(); if (seenTlds.add(tld)) { - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); if (!isSuperuser) { verifyNotInPredelegation(Registry.get(tld), now); } diff --git a/java/google/registry/flows/domain/DomainCreateFlow.java b/java/google/registry/flows/domain/DomainCreateFlow.java index 92a421109..54f6cc5ff 100644 --- a/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/java/google/registry/flows/domain/DomainCreateFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences; @@ -52,9 +53,10 @@ import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.StatusProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; import google.registry.model.ImmutableObject; @@ -99,7 +101,7 @@ import org.joda.time.DateTime; * @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException} * @error {@link google.registry.flows.exceptions.ResourceAlreadyExistsException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException} - * @error {@link google.registry.flows.LoggedInFlow.UndeclaredServiceExtensionException} + * @error {@link google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException} * @error {@link google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainCreateFlow.SignedMarksNotAcceptedInCurrentPhaseException} * @error {@link DomainFlowUtils.AcceptedTooLongAgoException} @@ -147,11 +149,12 @@ import org.joda.time.DateTime; * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} */ -public class DomainCreateFlow extends LoggedInFlow implements TransactionalFlow { +public class DomainCreateFlow extends Flow implements TransactionalFlow { private static final Set QLP_SMD_ALLOWED_STATES = Sets.immutableEnumSet(TldState.SUNRISE, TldState.SUNRUSH); + @Inject ExtensionManager extensionManager; @Inject AuthInfo authInfo; @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @@ -160,17 +163,15 @@ public class DomainCreateFlow extends LoggedInFlow implements TransactionalFlow @Inject DomainCreateFlow() {} @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - registerExtensions( + public final EppOutput run() throws EppException { + extensionManager.register( SecDnsCreateExtension.class, FlagsCreateCommandExtension.class, MetadataExtension.class, LaunchCreateExtension.class); - } - - @Override - public final EppOutput run() throws EppException { + extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Create command = cloneAndLinkReferences((Create) resourceCommand, now); Period period = command.getPeriod(); verifyUnitIsYears(period); @@ -204,7 +205,7 @@ public class DomainCreateFlow extends LoggedInFlow implements TransactionalFlow // notice without specifying a claims key, ignore the registry phase, and override blocks on // registering premium domains. if (!isSuperuser) { - checkAllowedAccessToTld(getAllowedTlds(), registry.getTldStr()); + checkAllowedAccessToTld(clientId, registry.getTldStr()); if (launchCreate != null) { verifyLaunchPhaseMatchesRegistryPhase(registry, launchCreate, now); } diff --git a/java/google/registry/flows/domain/DomainDeleteFlow.java b/java/google/registry/flows/domain/DomainDeleteFlow.java index de0a531e9..9435bf43a 100644 --- a/java/google/registry/flows/domain/DomainDeleteFlow.java +++ b/java/google/registry/flows/domain/DomainDeleteFlow.java @@ -15,6 +15,7 @@ package google.registry.flows.domain; import static com.google.common.base.Preconditions.checkNotNull; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.handlePendingTransferOnDelete; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.prepareDeletedResourceAsBuilder; @@ -38,9 +39,10 @@ import com.googlecode.objectify.Key; import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.EppException.AssociationProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; @@ -55,7 +57,7 @@ import google.registry.model.domain.fee11.FeeDeleteResponseExtensionV11; import google.registry.model.domain.fee12.FeeDeleteResponseExtensionV12; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; -import google.registry.model.domain.secdns.SecDnsUpdateExtension; +import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; import google.registry.model.eppcommon.StatusValue; @@ -82,7 +84,7 @@ import org.joda.time.DateTime; * @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainDeleteFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainDeleteFlow extends Flow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.LINKED, @@ -90,6 +92,7 @@ public final class DomainDeleteFlow extends LoggedInFlow implements Transactiona StatusValue.PENDING_DELETE, StatusValue.SERVER_DELETE_PROHIBITED); + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -97,15 +100,11 @@ public final class DomainDeleteFlow extends LoggedInFlow implements Transactiona @Inject DnsQueue dnsQueue; @Inject DomainDeleteFlow() {} - @Override - @SuppressWarnings("unchecked") - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - registerExtensions(SecDnsUpdateExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class, SecDnsCreateExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); // Loads the target resource if it exists DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); Registry registry = Registry.get(existingDomain.getTld()); @@ -165,7 +164,7 @@ public final class DomainDeleteFlow extends LoggedInFlow implements Transactiona verifyResourceOwnership(clientId, existingDomain); verifyNotInPredelegation(registry, now); } - checkAllowedAccessToTld(getAllowedTlds(), registry.getTld().toString()); + checkAllowedAccessToTld(clientId, registry.getTld().toString()); if (!existingDomain.getSubordinateHosts().isEmpty()) { throw new DomainToDeleteHasHostsException(); } diff --git a/java/google/registry/flows/domain/DomainFlowUtils.java b/java/google/registry/flows/domain/DomainFlowUtils.java index e565fe11d..c1ea80dfa 100644 --- a/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/java/google/registry/flows/domain/DomainFlowUtils.java @@ -247,9 +247,9 @@ public class DomainFlowUtils { } /** Check if the registrar running the flow has access to the TLD in question. */ - public static void checkAllowedAccessToTld(Set allowedTlds, String tld) + public static void checkAllowedAccessToTld(String clientId, String tld) throws EppException { - if (!allowedTlds.contains(tld)) { + if (!Registrar.loadByClientId(clientId).getAllowedTlds().contains(tld)) { throw new DomainFlowUtils.NotAuthorizedForTldException(tld); } } diff --git a/java/google/registry/flows/domain/DomainInfoFlow.java b/java/google/registry/flows/domain/DomainInfoFlow.java index f439b74ca..f04b6b6b5 100644 --- a/java/google/registry/flows/domain/DomainInfoFlow.java +++ b/java/google/registry/flows/domain/DomainInfoFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent; @@ -26,9 +27,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.net.InternetDomainName; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.model.domain.DomainCommand.Info; import google.registry.model.domain.DomainCommand.Info.HostsRequest; import google.registry.model.domain.DomainResource; @@ -59,21 +61,20 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException} * @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException} */ -public final class DomainInfoFlow extends LoggedInFlow { +public final class DomainInfoFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject ResourceCommand resourceCommand; @Inject DomainInfoFlow() {} - @Override - protected void initLoggedInFlow() throws EppException { - registerExtensions(FeeInfoCommandExtensionV06.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(FeeInfoCommandExtensionV06.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, domain); return createOutput( diff --git a/java/google/registry/flows/domain/DomainRenewFlow.java b/java/google/registry/flows/domain/DomainRenewFlow.java index 47bacfacb..10ccd2666 100644 --- a/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/java/google/registry/flows/domain/DomainRenewFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -38,9 +39,10 @@ import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.EppException.ObjectPendingTransferException; import google.registry.flows.EppException.ParameterValueRangeErrorException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; import google.registry.model.billing.BillingEvent; @@ -95,7 +97,7 @@ import org.joda.time.DateTime; * @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException} * @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException} */ -public final class DomainRenewFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainRenewFlow extends Flow implements TransactionalFlow { private static final ImmutableSet RENEW_DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.CLIENT_RENEW_PROHIBITED, @@ -103,20 +105,19 @@ public final class DomainRenewFlow extends LoggedInFlow implements Transactional StatusValue.SERVER_RENEW_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DomainRenewFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - registerExtensions(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.registerAsGroup(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Renew command = (Renew) resourceCommand; // Loads the target resource if it exists DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); @@ -185,7 +186,7 @@ public final class DomainRenewFlow extends LoggedInFlow implements Transactional if (!isSuperuser) { verifyResourceOwnership(clientId, existingDomain); } - checkAllowedAccessToTld(getAllowedTlds(), existingDomain.getTld()); + checkAllowedAccessToTld(clientId, existingDomain.getTld()); // Verify that the resource does not have a pending transfer on it. if (existingDomain.getTransferData().getTransferStatus() == TransferStatus.PENDING) { throw new DomainHasPendingTransferException(targetId); diff --git a/java/google/registry/flows/domain/DomainRestoreRequestFlow.java b/java/google/registry/flows/domain/DomainRestoreRequestFlow.java index c875b777e..6b02e8d4b 100644 --- a/java/google/registry/flows/domain/DomainRestoreRequestFlow.java +++ b/java/google/registry/flows/domain/DomainRestoreRequestFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.updateForeignKeyIndexDeletionTime; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -38,9 +39,10 @@ import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.StatusProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; import google.registry.model.ImmutableObject; @@ -101,9 +103,10 @@ import org.joda.time.DateTime; * @error {@link DomainRestoreRequestFlow.DomainNotEligibleForRestoreException} * @error {@link DomainRestoreRequestFlow.RestoreCommandIncludesChangesException} */ -public final class DomainRestoreRequestFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainRestoreRequestFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -111,14 +114,12 @@ public final class DomainRestoreRequestFlow extends LoggedInFlow implements Tran @Inject DnsQueue dnsQueue; @Inject DomainRestoreRequestFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class, RgpUpdateExtension.class); - registerExtensions(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class, RgpUpdateExtension.class); + extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Update command = (Update) resourceCommand; DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); Money restoreCost = Registry.get(existingDomain.getTld()).getStandardRestoreCost(); @@ -197,7 +198,7 @@ public final class DomainRestoreRequestFlow extends LoggedInFlow implements Tran if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) { throw new DomainNotEligibleForRestoreException(); } - checkAllowedAccessToTld(getAllowedTlds(), existingDomain.getTld()); + checkAllowedAccessToTld(clientId, existingDomain.getTld()); validateFeeChallenge(targetId, existingDomain.getTld(), now, feeUpdate, restoreCost, renewCost); } diff --git a/java/google/registry/flows/domain/DomainTransferApproveFlow.java b/java/google/registry/flows/domain/DomainTransferApproveFlow.java index 3e76fd365..02c909169 100644 --- a/java/google/registry/flows/domain/DomainTransferApproveFlow.java +++ b/java/google/registry/flows/domain/DomainTransferApproveFlow.java @@ -16,6 +16,7 @@ package google.registry.flows.domain; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.getOnlyElement; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.approvePendingTransfer; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer; @@ -36,9 +37,10 @@ import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; @@ -76,31 +78,30 @@ import org.joda.time.DateTime; * @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainTransferApproveFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainTransferApproveFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DomainTransferApproveFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - /** *

The logic in this flow, which handles client approvals, very closely parallels the logic in * {@link DomainResource#cloneProjectedAtTime} which handles implicit server approvals. */ @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyHasPendingTransfer(existingDomain); verifyResourceOwnership(clientId, existingDomain); String tld = existingDomain.getTld(); - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); HistoryEntry historyEntry = historyBuilder .setType(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE) .setModificationTime(now) diff --git a/java/google/registry/flows/domain/DomainTransferCancelFlow.java b/java/google/registry/flows/domain/DomainTransferCancelFlow.java index 2929de5ec..25dfc837c 100644 --- a/java/google/registry/flows/domain/DomainTransferCancelFlow.java +++ b/java/google/registry/flows/domain/DomainTransferCancelFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.denyPendingTransfer; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer; @@ -30,9 +31,10 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainResource; @@ -61,26 +63,25 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NotTransferInitiatorException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainTransferCancelFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainTransferCancelFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DomainTransferCancelFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyHasPendingTransfer(existingDomain); verifyIsGainingRegistrar(existingDomain, clientId); - checkAllowedAccessToTld(getAllowedTlds(), existingDomain.getTld()); + checkAllowedAccessToTld(clientId, existingDomain.getTld()); HistoryEntry historyEntry = historyBuilder .setType(HistoryEntry.Type.DOMAIN_TRANSFER_CANCEL) .setModificationTime(now) diff --git a/java/google/registry/flows/domain/DomainTransferQueryFlow.java b/java/google/registry/flows/domain/DomainTransferQueryFlow.java index fbdaf9447..975bef0b6 100644 --- a/java/google/registry/flows/domain/DomainTransferQueryFlow.java +++ b/java/google/registry/flows/domain/DomainTransferQueryFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; @@ -22,9 +23,10 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.exceptions.NoTransferHistoryToQueryException; import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; import google.registry.model.domain.DomainResource; @@ -50,8 +52,9 @@ import org.joda.time.DateTime; * @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException} * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} */ -public final class DomainTransferQueryFlow extends LoggedInFlow { +public final class DomainTransferQueryFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -59,6 +62,8 @@ public final class DomainTransferQueryFlow extends LoggedInFlow { @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, domain); // Most of the fields on the transfer response are required, so there's no way to return valid diff --git a/java/google/registry/flows/domain/DomainTransferRejectFlow.java b/java/google/registry/flows/domain/DomainTransferRejectFlow.java index 1174492f7..b2cdbdc77 100644 --- a/java/google/registry/flows/domain/DomainTransferRejectFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRejectFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.domain; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.denyPendingTransfer; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer; @@ -30,9 +31,10 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainResource; @@ -61,21 +63,20 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainTransferRejectFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainTransferRejectFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DomainTransferRejectFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); HistoryEntry historyEntry = historyBuilder .setType(HistoryEntry.Type.DOMAIN_TRANSFER_REJECT) @@ -85,7 +86,7 @@ public final class DomainTransferRejectFlow extends LoggedInFlow implements Tran verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyHasPendingTransfer(existingDomain); verifyResourceOwnership(clientId, existingDomain); - checkAllowedAccessToTld(getAllowedTlds(), existingDomain.getTld()); + checkAllowedAccessToTld(clientId, existingDomain.getTld()); DomainResource newDomain = denyPendingTransfer(existingDomain, TransferStatus.CLIENT_REJECTED, now); ofy().save().entities( diff --git a/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 3c9bccc2d..ac8c3b33d 100644 --- a/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -16,6 +16,7 @@ package google.registry.flows.domain; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.getOnlyElement; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyRequiredAuthInfoForResourceTransfer; @@ -39,9 +40,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AlreadyPendingTransferException; import google.registry.flows.exceptions.ObjectAlreadySponsoredException; @@ -104,7 +106,7 @@ import org.joda.time.Duration; * @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} */ -public final class DomainTransferRequestFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainTransferRequestFlow extends Flow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.CLIENT_TRANSFER_PROHIBITED, @@ -112,20 +114,19 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra StatusValue.SERVER_TRANSFER_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String gainingClientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DomainTransferRequestFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class, FlagsTransferCommandExtension.class); - registerExtensions(FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(FlagsTransferCommandExtension.class, MetadataExtension.class); + extensionManager.registerAsGroup(FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(gainingClientId); Period period = ((Transfer) resourceCommand).getPeriod(); int years = period.getValue(); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); @@ -194,7 +195,7 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra if (gainingClientId.equals(existingDomain.getCurrentSponsorClientId())) { throw new ObjectAlreadySponsoredException(); } - checkAllowedAccessToTld(getAllowedTlds(), existingDomain.getTld()); + checkAllowedAccessToTld(gainingClientId, existingDomain.getTld()); verifyUnitIsYears(period); if (!isSuperuser) { verifyPremiumNameIsNotBlocked(targetId, now, gainingClientId); diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java index 4c5b0fb0c..ae44a027f 100644 --- a/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -15,6 +15,7 @@ package google.registry.flows.domain; import static com.google.common.collect.Sets.symmetricDifference; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -44,9 +45,10 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.dns.DnsQueue; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeUpdateException; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; @@ -116,7 +118,7 @@ import org.joda.time.DateTime; * @error {@link DomainFlowUtils.TooManyNameserversException} * @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException} */ -public final class DomainUpdateFlow extends LoggedInFlow implements TransactionalFlow { +public final class DomainUpdateFlow extends Flow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -128,6 +130,7 @@ public final class DomainUpdateFlow extends LoggedInFlow implements Transactiona StatusValue.SERVER_UPDATE_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -135,15 +138,15 @@ public final class DomainUpdateFlow extends LoggedInFlow implements Transactiona @Inject DnsQueue dnsQueue; @Inject DomainUpdateFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - registerExtensions( - MetadataExtension.class, SecDnsUpdateExtension.class, FlagsUpdateCommandExtension.class); - } - @Override public EppOutput run() throws EppException { + extensionManager.register( + FlagsUpdateCommandExtension.class, + MetadataExtension.class, + SecDnsUpdateExtension.class); + extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Update command = cloneAndLinkReferences((Update) resourceCommand, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyUpdateAllowed(command, existingDomain); @@ -184,7 +187,7 @@ public final class DomainUpdateFlow extends LoggedInFlow implements Transactiona verifyStatusChangesAreClientSettable(command); } String tld = existingDomain.getTld(); - checkAllowedAccessToTld(getAllowedTlds(), tld); + checkAllowedAccessToTld(clientId, tld); EppCommandOperations commandOperations = TldSpecificLogicProxy.getUpdatePrice( Registry.get(tld), targetId, clientId, now, eppInput); diff --git a/java/google/registry/flows/host/HostCheckFlow.java b/java/google/registry/flows/host/HostCheckFlow.java index 09fc2f374..d26e0c6fc 100644 --- a/java/google/registry/flows/host/HostCheckFlow.java +++ b/java/google/registry/flows/host/HostCheckFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.host; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.eppoutput.Result.Code.SUCCESS; @@ -21,7 +22,9 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.collect.ImmutableList; import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; -import google.registry.flows.LoggedInFlow; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; +import google.registry.flows.FlowModule.ClientId; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CheckData.HostCheck; import google.registry.model.eppoutput.CheckData.HostCheckData; @@ -39,14 +42,18 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} */ -public final class HostCheckFlow extends LoggedInFlow { +public final class HostCheckFlow extends Flow { @Inject ResourceCommand resourceCommand; + @Inject @ClientId String clientId; + @Inject ExtensionManager extensionManager; @Inject @Config("maxChecks") int maxChecks; @Inject HostCheckFlow() {} @Override protected final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); Set existingIds = checkResourcesExist(HostResource.class, targetIds, now); diff --git a/java/google/registry/flows/host/HostCreateFlow.java b/java/google/registry/flows/host/HostCreateFlow.java index 95022cd3d..f8889e01e 100644 --- a/java/google/registry/flows/host/HostCreateFlow.java +++ b/java/google/registry/flows/host/HostCreateFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.host; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist; import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain; import static google.registry.flows.host.HostFlowUtils.validateHostName; @@ -31,9 +32,10 @@ import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainResource; @@ -68,23 +70,21 @@ import javax.inject.Inject; * @error {@link SubordinateHostMustHaveIpException} * @error {@link UnexpectedExternalHostIpException} */ -public final class HostCreateFlow extends LoggedInFlow implements TransactionalFlow { +public final class HostCreateFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DnsQueue dnsQueue; @Inject HostCreateFlow() {} - @Override - @SuppressWarnings("unchecked") - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override protected final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Create command = (Create) resourceCommand; verifyResourceDoesNotExist(HostResource.class, targetId, now); // The superordinate domain of the host object if creating an in-bailiwick host, or null if diff --git a/java/google/registry/flows/host/HostDeleteFlow.java b/java/google/registry/flows/host/HostDeleteFlow.java index b8d4cbfe8..e6f1a21d3 100644 --- a/java/google/registry/flows/host/HostDeleteFlow.java +++ b/java/google/registry/flows/host/HostDeleteFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.host; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; @@ -27,9 +28,10 @@ import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowEnqueuer; import google.registry.model.domain.DomainBase; @@ -55,7 +57,7 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} * @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} */ -public final class HostDeleteFlow extends LoggedInFlow implements TransactionalFlow { +public final class HostDeleteFlow extends Flow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.LINKED, @@ -70,20 +72,19 @@ public final class HostDeleteFlow extends LoggedInFlow implements TransactionalF return domain.getNameservers(); }}; - @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; @Inject HostDeleteFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); failfastForAsyncDelete(targetId, now, HostResource.class, GET_NAMESERVERS); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES); diff --git a/java/google/registry/flows/host/HostInfoFlow.java b/java/google/registry/flows/host/HostInfoFlow.java index f27140496..aedc4c094 100644 --- a/java/google/registry/flows/host/HostInfoFlow.java +++ b/java/google/registry/flows/host/HostInfoFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.host; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; @@ -21,8 +22,10 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; +import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppoutput.EppOutput; import google.registry.model.host.HostResource; @@ -36,14 +39,18 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} */ -public final class HostInfoFlow extends LoggedInFlow { +public final class HostInfoFlow extends Flow { + @Inject ExtensionManager extensionManager; + @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject Optional authInfo; @Inject HostInfoFlow() {} @Override public EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, host); return createOutput(SUCCESS, cloneResourceWithLinkedStatus(host, now)); diff --git a/java/google/registry/flows/host/HostUpdateFlow.java b/java/google/registry/flows/host/HostUpdateFlow.java index 868e60fb6..2498f11ca 100644 --- a/java/google/registry/flows/host/HostUpdateFlow.java +++ b/java/google/registry/flows/host/HostUpdateFlow.java @@ -15,6 +15,7 @@ package google.registry.flows.host; import static com.google.common.base.MoreObjects.firstNonNull; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; @@ -37,9 +38,10 @@ import google.registry.flows.EppException.ObjectAlreadyExistsException; import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.StatusProhibitsOperationException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowEnqueuer; import google.registry.flows.exceptions.AddRemoveSameValueEppException; @@ -88,7 +90,7 @@ import javax.inject.Inject; * @error {@link RenameHostToExternalRemoveIpException} * @error {@link RenameHostToSubordinateRequiresIpException} */ -public final class HostUpdateFlow extends LoggedInFlow implements TransactionalFlow { +public final class HostUpdateFlow extends Flow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -100,6 +102,7 @@ public final class HostUpdateFlow extends LoggedInFlow implements TransactionalF StatusValue.SERVER_UPDATE_PROHIBITED); @Inject ResourceCommand resourceCommand; + @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @@ -108,13 +111,11 @@ public final class HostUpdateFlow extends LoggedInFlow implements TransactionalF @Inject DnsQueue dnsQueue; @Inject HostUpdateFlow() {} - @Override - protected final void initLoggedInFlow() throws EppException { - registerExtensions(MetadataExtension.class); - } - @Override public final EppOutput run() throws EppException { + extensionManager.register(MetadataExtension.class); + extensionManager.validate(); + validateClientIsLoggedIn(clientId); Update command = (Update) resourceCommand; String suppliedNewHostName = command.getInnerChange().getFullyQualifiedHostName(); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); diff --git a/java/google/registry/flows/poll/PollAckFlow.java b/java/google/registry/flows/poll/PollAckFlow.java index 64bb0f091..22bd3e042 100644 --- a/java/google/registry/flows/poll/PollAckFlow.java +++ b/java/google/registry/flows/poll/PollAckFlow.java @@ -15,6 +15,7 @@ package google.registry.flows.poll; import static com.google.common.base.Preconditions.checkState; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery; import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES; @@ -28,9 +29,10 @@ import google.registry.flows.EppException.AuthorizationErrorException; import google.registry.flows.EppException.ObjectDoesNotExistException; import google.registry.flows.EppException.ParameterValueSyntaxErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.PollMessageId; -import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.model.eppoutput.EppOutput; import google.registry.model.poll.MessageQueueInfo; @@ -53,14 +55,17 @@ import org.joda.time.DateTime; * @error {@link PollAckFlow.MissingMessageIdException} * @error {@link PollAckFlow.NotAuthorizedToAckMessageException} */ -public class PollAckFlow extends LoggedInFlow implements TransactionalFlow { +public class PollAckFlow extends Flow implements TransactionalFlow { + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @PollMessageId String messageId; @Inject PollAckFlow() {} @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); if (messageId.isEmpty()) { throw new MissingMessageIdException(); } diff --git a/java/google/registry/flows/poll/PollRequestFlow.java b/java/google/registry/flows/poll/PollRequestFlow.java index 64b3f8432..74d2e2699 100644 --- a/java/google/registry/flows/poll/PollRequestFlow.java +++ b/java/google/registry/flows/poll/PollRequestFlow.java @@ -14,6 +14,7 @@ package google.registry.flows.poll; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery; import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACK_MESSAGE; import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES; @@ -22,9 +23,10 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValueSyntaxErrorException; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.PollMessageId; -import google.registry.flows.LoggedInFlow; import google.registry.model.eppoutput.EppOutput; import google.registry.model.poll.MessageQueueInfo; import google.registry.model.poll.PollMessage; @@ -42,14 +44,17 @@ import javax.inject.Inject; * * @error {@link PollRequestFlow.UnexpectedMessageIdException} */ -public class PollRequestFlow extends LoggedInFlow { +public class PollRequestFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @PollMessageId String messageId; @Inject PollRequestFlow() {} @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); if (!messageId.isEmpty()) { throw new UnexpectedMessageIdException(); } diff --git a/java/google/registry/flows/session/HelloFlow.java b/java/google/registry/flows/session/HelloFlow.java index 6bfdd3bd7..e6ae67e01 100644 --- a/java/google/registry/flows/session/HelloFlow.java +++ b/java/google/registry/flows/session/HelloFlow.java @@ -14,6 +14,8 @@ package google.registry.flows.session; +import google.registry.flows.EppException; +import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.Greeting; @@ -22,10 +24,12 @@ import javax.inject.Inject; /** A flow for an Epp "hello". */ public class HelloFlow extends Flow { + @Inject ExtensionManager extensionManager; @Inject HelloFlow() {} @Override - public EppOutput run() { + public EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. return EppOutput.create(Greeting.create(now)); } } diff --git a/java/google/registry/flows/session/LoginFlow.java b/java/google/registry/flows/session/LoginFlow.java index 5fe3cf085..6cdd17f4d 100644 --- a/java/google/registry/flows/session/LoginFlow.java +++ b/java/google/registry/flows/session/LoginFlow.java @@ -27,6 +27,7 @@ import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.UnimplementedExtensionException; import google.registry.flows.EppException.UnimplementedObjectServiceException; import google.registry.flows.EppException.UnimplementedOptionException; +import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.model.eppcommon.ProtocolDefinition; @@ -68,6 +69,7 @@ public class LoginFlow extends Flow { /** Maximum number of failed login attempts allowed per connection. */ private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3; + @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject LoginFlow() {} @@ -84,6 +86,7 @@ public class LoginFlow extends Flow { /** Run the flow without bothering to log errors. The {@link #run} method will do that for us. */ public final EppOutput runWithoutLogging() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. Login login = (Login) eppInput.getCommandWrapper().getCommand(); if (!clientId.isEmpty()) { throw new AlreadyLoggedInException(); diff --git a/java/google/registry/flows/session/LogoutFlow.java b/java/google/registry/flows/session/LogoutFlow.java index 884376483..bb38188fb 100644 --- a/java/google/registry/flows/session/LogoutFlow.java +++ b/java/google/registry/flows/session/LogoutFlow.java @@ -14,24 +14,31 @@ package google.registry.flows.session; +import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE; import google.registry.flows.EppException; -import google.registry.flows.LoggedInFlow; +import google.registry.flows.ExtensionManager; +import google.registry.flows.Flow; +import google.registry.flows.FlowModule.ClientId; import google.registry.model.eppoutput.EppOutput; import javax.inject.Inject; /** * An EPP flow for logout. * - * @error {@link google.registry.flows.LoggedInFlow.NotLoggedInException} + * @error {@link google.registry.flows.FlowUtils.NotLoggedInException} */ -public class LogoutFlow extends LoggedInFlow { +public class LogoutFlow extends Flow { + @Inject ExtensionManager extensionManager; + @Inject @ClientId String clientId; @Inject LogoutFlow() {} @Override public final EppOutput run() throws EppException { + extensionManager.validate(); // There are no legal extensions for this flow. + validateClientIsLoggedIn(clientId); sessionMetadata.invalidate(); return createOutput(SUCCESS_AND_CLOSE); } diff --git a/javatests/google/registry/flows/ExtensionManagerTest.java b/javatests/google/registry/flows/ExtensionManagerTest.java new file mode 100644 index 000000000..b11f9ec84 --- /dev/null +++ b/javatests/google/registry/flows/ExtensionManagerTest.java @@ -0,0 +1,218 @@ +// Copyright 2016 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.flows; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.testing.TestLogHandler; +import google.registry.flows.EppException.UnimplementedExtensionException; +import google.registry.flows.ExtensionManager.UndeclaredServiceExtensionException; +import google.registry.flows.ExtensionManager.UnsupportedRepeatedExtensionException; +import google.registry.flows.exceptions.OnlyToolCanPassMetadataException; +import google.registry.flows.session.HelloFlow; +import google.registry.model.domain.allocate.AllocateCreateExtension; +import google.registry.model.domain.fee06.FeeInfoCommandExtensionV06; +import google.registry.model.domain.launch.LaunchCreateExtension; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; +import google.registry.model.eppinput.EppInput; +import google.registry.model.eppinput.EppInput.CommandExtension; +import google.registry.testing.AppEngineRule; +import google.registry.testing.ExceptionRule; +import google.registry.util.TypeUtils; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link ExtensionManager}. */ +@RunWith(JUnit4.class) +public class ExtensionManagerTest { + + @Rule + public final AppEngineRule appEngine = AppEngineRule.builder() + .withDatastore() + .build(); + + @Rule + public final ExceptionRule thrown = new ExceptionRule(); + + @Test + public void testDuplicateExtensionsForbidden() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris() + .setSuppliedExtensions( + MetadataExtension.class, + LaunchCreateExtension.class, + MetadataExtension.class) + .build(); + manager.register(MetadataExtension.class, LaunchCreateExtension.class); + thrown.expect(UnsupportedRepeatedExtensionException.class); + manager.validate(); + } + + @Test + public void testMultipleExtensionsFromSameGroupForbidden() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris(ServiceExtension.FEE_0_6.getUri()) + .setSuppliedExtensions( + MetadataExtension.class, + LaunchCreateExtension.class, + AllocateCreateExtension.class) + .build(); + manager.register(MetadataExtension.class); + manager.registerAsGroup( + ImmutableList.of(LaunchCreateExtension.class, AllocateCreateExtension.class)); + thrown.expect(UnsupportedRepeatedExtensionException.class); + manager.validate(); + } + + @Test + public void testUndeclaredExtensionsLogged() throws Exception { + TestLogHandler handler = new TestLogHandler(); + Logger.getLogger(ExtensionManager.class.getCanonicalName()).addHandler(handler); + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris() + .setSuppliedExtensions(MetadataExtension.class) + .build(); + manager.register(MetadataExtension.class); + manager.validate(); + ImmutableList.Builder logMessages = new ImmutableList.Builder<>(); + for (LogRecord record : handler.getStoredLogRecords()) { + logMessages.add(record.getMessage()); + } + assertThat(logMessages.build()).contains( + "Client clientId is attempting to run HelloFlow without declaring " + + "URIs [urn:google:params:xml:ns:metadata-1.0] on login"); + } + + @Test + public void testBlacklistedExtensions_forbiddenWhenUndeclared() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris() + .setSuppliedExtensions(FeeInfoCommandExtensionV06.class) + .build(); + manager.register(FeeInfoCommandExtensionV06.class); + thrown.expect(UndeclaredServiceExtensionException.class); + manager.validate(); + } + + @Test + public void testBlacklistedExtensions_allowedWhenDeclared() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris(ServiceExtension.FEE_0_6.getUri()) + .setSuppliedExtensions(FeeInfoCommandExtensionV06.class) + .build(); + manager.register(FeeInfoCommandExtensionV06.class); + manager.validate(); + } + + @Test + public void testMetadataExtension_allowedForToolSource() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris() + .setSuppliedExtensions(MetadataExtension.class) + .build(); + manager.register(MetadataExtension.class); + manager.validate(); + } + + @Test + public void testMetadataExtension_forbiddenWhenNotToolSource() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.CONSOLE) + .setDeclaredUris() + .setSuppliedExtensions(MetadataExtension.class) + .build(); + manager.register(MetadataExtension.class); + thrown.expect(OnlyToolCanPassMetadataException.class); + manager.validate(); + } + + @Test + public void testUnimplementedExtensionsForbidden() throws Exception { + ExtensionManager manager = new TestInstanceBuilder() + .setEppRequestSource(EppRequestSource.TOOL) + .setDeclaredUris() + .setSuppliedExtensions(LaunchCreateExtension.class) + .build(); + thrown.expect(UnimplementedExtensionException.class); + manager.validate(); + } + + /** A builder for a test-ready {@link ExtensionManager} instance. */ + private static class TestInstanceBuilder { + + ExtensionManager manager = new ExtensionManager(); + + TestInstanceBuilder setEppRequestSource(EppRequestSource eppRequestSource) { + manager.eppRequestSource = eppRequestSource; + return this; + } + + TestInstanceBuilder setDeclaredUris(String... declaredUris) { + manager.sessionMetadata = + new StatelessRequestSessionMetadata("clientId", ImmutableSet.copyOf(declaredUris)); + return this; + } + + @SafeVarargs + final TestInstanceBuilder setSuppliedExtensions( + Class... suppliedExtensionClasses) { + manager.eppInput = new FakeEppInput(suppliedExtensionClasses); + return this; + } + + ExtensionManager build() { + manager.flowClass = HelloFlow.class; + manager.clientId = manager.sessionMetadata.getClientId(); + return manager; + } + } + + /** A minimal fake {@link EppInput} that presents the given extensions. */ + private static class FakeEppInput extends EppInput { + + private final ImmutableList suppliedExtensions; + + @SafeVarargs + FakeEppInput(Class... suppliedExtensionClasses) { + ImmutableList.Builder instancesBuilder = new ImmutableList.Builder<>(); + for (Class clazz : suppliedExtensionClasses) { + instancesBuilder.add(TypeUtils.instantiate(clazz)); + } + suppliedExtensions = instancesBuilder.build(); + } + + @Override + public CommandWrapper getCommandWrapper() { + return new CommandWrapper() { + @Override + public ImmutableList getExtensions() { + return suppliedExtensions; + }}; + } + } +} diff --git a/javatests/google/registry/flows/ResourceFlowTestCase.java b/javatests/google/registry/flows/ResourceFlowTestCase.java index 12b3bbedb..e3c33ccc0 100644 --- a/javatests/google/registry/flows/ResourceFlowTestCase.java +++ b/javatests/google/registry/flows/ResourceFlowTestCase.java @@ -25,7 +25,7 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.googlecode.objectify.Key; -import google.registry.flows.EppException.CommandUseErrorException; +import google.registry.flows.FlowUtils.NotLoggedInException; import google.registry.model.EppResource; import google.registry.model.EppResourceUtils; import google.registry.model.domain.DomainApplication; @@ -114,7 +114,7 @@ public abstract class ResourceFlowTestCase