google.registry.flows.ExtensionManager.java Source code

Java tutorial

Introduction

Here is the source code for google.registry.flows.ExtensionManager.java

Source

// 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.Iterables.any;
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.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
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.Set;
import javax.inject.Inject;

/**
 * Helper to validate extensions on an EPP command.
 *
 * <p>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<String> UNDECLARED_URIS_BLACKLIST = FEE_EXTENSION_URIS;

    private final ImmutableSet.Builder<Class<? extends CommandExtension>> implementedBuilder = new ImmutableSet.Builder<>();

    @Inject
    EppInput eppInput;
    @Inject
    SessionMetadata sessionMetadata;
    @Inject
    @ClientId
    String clientId;
    @Inject
    Class<? extends Flow> flowClass;
    @Inject
    EppRequestSource eppRequestSource;

    @Inject
    ExtensionManager() {
    }

    @SafeVarargs
    public final void register(Class<? extends CommandExtension>... extension) {
        implementedBuilder.add(extension);
    }

    public void validate() throws EppException {
        ImmutableSet.Builder<Class<? extends CommandExtension>> suppliedBuilder = new ImmutableSet.Builder<>();
        for (CommandExtension extension : eppInput.getCommandWrapper().getExtensions()) {
            suppliedBuilder.add(extension.getClass());
        }
        ImmutableSet<Class<? extends CommandExtension>> suppliedExtensions = suppliedBuilder.build();
        ImmutableSet<Class<? extends CommandExtension>> implementedExtensions = implementedBuilder.build();
        ImmutableList<CommandExtension> suppliedExtensionInstances = eppInput.getCommandWrapper().getExtensions();
        checkForUndeclaredExtensions(suppliedExtensions);
        checkForRestrictedExtensions(suppliedExtensions);
        checkForDuplicateExtensions(suppliedExtensionInstances, suppliedExtensions);
        checkForUnimplementedExtensions(suppliedExtensionInstances, implementedExtensions);
    }

    private void checkForUndeclaredExtensions(ImmutableSet<Class<? extends CommandExtension>> suppliedExtensions)
            throws UndeclaredServiceExtensionException {
        ImmutableSet.Builder<String> suppliedUris = new ImmutableSet.Builder<>();
        for (Class<? extends CommandExtension> extension : suppliedExtensions) {
            suppliedUris.add(getCommandExtensionUri(extension));
        }
        Set<String> declaredUris = sessionMetadata.getServiceExtensionUris();
        Set<String> undeclaredUris = difference(suppliedUris.build(), declaredUris);
        if (undeclaredUris.isEmpty()) {
            return;
        }
        Set<String> 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<Class<? extends CommandExtension>> suppliedExtensions)
            throws OnlyToolCanPassMetadataException {
        if (suppliedExtensions.contains(MetadataExtension.class)
                && !eppRequestSource.equals(EppRequestSource.TOOL)) {
            throw new OnlyToolCanPassMetadataException();
        }
    }

    private static void checkForDuplicateExtensions(ImmutableList<CommandExtension> suppliedExtensionInstances,
            ImmutableSet<Class<? extends CommandExtension>> implementedExtensions)
            throws UnsupportedRepeatedExtensionException {
        for (Class<? extends CommandExtension> implemented : implementedExtensions) {
            if (FluentIterable.from(suppliedExtensionInstances).filter(implemented).size() > 1) {
                throw new UnsupportedRepeatedExtensionException();
            }
        }
    }

    private static void checkForUnimplementedExtensions(ImmutableList<CommandExtension> suppliedExtensionInstances,
            ImmutableSet<Class<? extends CommandExtension>> implementedExtensionClasses)
            throws UnimplementedExtensionException {
        ImmutableSet.Builder<Class<? extends CommandExtension>> unimplementedExtensionsBuilder = new ImmutableSet.Builder<>();
        for (final CommandExtension instance : suppliedExtensionInstances) {
            if (!any(implementedExtensionClasses, new Predicate<Class<? extends CommandExtension>>() {
                @Override
                public boolean apply(Class<? extends CommandExtension> implementedExtensionClass) {
                    return implementedExtensionClass.isInstance(instance);
                }
            })) {
                unimplementedExtensionsBuilder.add(instance.getClass());
            }
        }
        ImmutableSet<Class<? extends CommandExtension>> unimplementedExtensions = unimplementedExtensionsBuilder
                .build();
        if (!unimplementedExtensions.isEmpty()) {
            logger.infofmt("Unimplemented extensions: %s", unimplementedExtensions);
            throw new UnimplementedExtensionException();
        }
    }

    /** Service extension(s) must be declared at login. */
    public static class UndeclaredServiceExtensionException extends CommandUseErrorException {
        public UndeclaredServiceExtensionException(Set<String> undeclaredUris) {
            super(String.format("Service extension(s) must be declared at login: %s",
                    Joiner.on(", ").join(undeclaredUris)));
        }
    }

    /** Unsupported repetition of an extension. */
    static class UnsupportedRepeatedExtensionException extends SyntaxErrorException {
        public UnsupportedRepeatedExtensionException() {
            super("Unsupported repetition of an extension");
        }
    }
}