org.codice.ddf.pax.web.jetty.JettyAuthenticator.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.pax.web.jetty.JettyAuthenticator.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.pax.web.jetty;

import ddf.security.SecurityConstants;
import ddf.security.Subject;
import java.io.IOException;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.codice.ddf.platform.filter.AuthenticationChallengeException;
import org.codice.ddf.platform.filter.AuthenticationException;
import org.codice.ddf.platform.filter.SecurityFilter;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.LoggerFactory;

public class JettyAuthenticator extends LoginAuthenticator {

    public static final String DDF_AUTH_METHOD = "DDF";
    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(JettyAuthenticator.class);
    private CopyOnWriteArraySet<String> keysOfInitializedSecurityFilters;

    public JettyAuthenticator() {
        super();
        keysOfInitializedSecurityFilters = new CopyOnWriteArraySet<>();
        _loginService = new DummyLoginService();
        _identityService = _loginService.getIdentityService();
    }

    @Override
    public void setConfiguration(AuthConfiguration configuration) {
        keysOfInitializedSecurityFilters.clear();
        if (configuration instanceof ConstraintSecurityHandler) {
            ((ConstraintSecurityHandler) configuration).setLoginService(_loginService);
            ((ConstraintSecurityHandler) configuration).setIdentityService(_identityService);
        }
    }

    @Override
    public String getAuthMethod() {
        return DDF_AUTH_METHOD;
    }

    @Override
    public Authentication validateRequest(ServletRequest servletRequest, ServletResponse servletResponse,
            boolean mandatory) throws ServerAuthException {

        TreeSet<ServiceReference<SecurityFilter>> sortedSecurityFilterServiceReferences = null;
        final BundleContext bundleContext = getContext();

        if (bundleContext == null) {
            throw new ServerAuthException(
                    "Unable to get BundleContext. No servlet SecurityFilters can be applied. Blocking the request processing.");
        }

        try {
            sortedSecurityFilterServiceReferences = new TreeSet<>(
                    bundleContext.getServiceReferences(SecurityFilter.class, null));
        } catch (InvalidSyntaxException ise) {
            LOGGER.debug("Should never get this exception as there is no filter being passed.");
        }

        if (!CollectionUtils.isEmpty(sortedSecurityFilterServiceReferences)) {
            LOGGER.debug("Found {} filter(s), now filtering...", sortedSecurityFilterServiceReferences.size());
            final ProxyFilterChain chain = new ProxyFilterChain();

            // Insert the SecurityFilters into the chain one at a time (from lowest service ranking
            // to highest service ranking). The SecurityFilter with the highest service-ranking will
            // end up at index 0 in the FilterChain, which means that the SecurityFilters will be
            // run in order of highest to lowest service ranking.
            for (ServiceReference<SecurityFilter> securityFilterServiceReference : sortedSecurityFilterServiceReferences) {
                final SecurityFilter securityFilter = bundleContext.getService(securityFilterServiceReference);

                if (!hasBeenInitialized(securityFilterServiceReference, bundleContext)) {
                    initializeSecurityFilter(bundleContext, securityFilterServiceReference, securityFilter);
                }
                chain.addSecurityFilter(securityFilter);
            }

            try {
                chain.doFilter(servletRequest, servletResponse);
            } catch (IOException e) {
                throw new ServerAuthException(
                        "Unable to process security filter. Blocking the request processing.");
            } catch (AuthenticationChallengeException e) {
                return new Authentication.Challenge() {
                };
            } catch (AuthenticationException e) {
                return new Authentication.Failure() {
                };
            }
        } else {
            LOGGER.debug("Did not find any SecurityFilters. Send auth failure...");
            return new Authentication.Failure() {
            };
        }

        Subject subject = (Subject) servletRequest.getAttribute(SecurityConstants.SECURITY_SUBJECT);
        UserIdentity userIdentity = new JettyUserIdentity(getSecuritySubject(subject));
        return new JettyAuthenticatedUser(userIdentity);
    }

    @Nullable
    protected BundleContext getContext() {
        final Bundle cxfBundle = FrameworkUtil.getBundle(JettyAuthenticator.class);
        if (cxfBundle != null) {
            return cxfBundle.getBundleContext();
        }
        return null;
    }

    @Nullable
    private javax.security.auth.Subject getSecuritySubject(@Nullable Subject subject) {
        if (subject == null) {
            return null;
        }
        HashSet emptySet = new HashSet();
        HashSet subjectPrincipal = new HashSet();
        subjectPrincipal.add(subject);
        return new javax.security.auth.Subject(true, subjectPrincipal, emptySet, emptySet);
    }

    private boolean hasBeenInitialized(final ServiceReference<SecurityFilter> securityFilterServiceReference,
            final BundleContext bundleContext) {
        return keysOfInitializedSecurityFilters
                .contains(getFilterKey(securityFilterServiceReference, bundleContext));
    }

    private void initializeSecurityFilter(BundleContext bundleContext,
            ServiceReference<SecurityFilter> securityFilterServiceReference, SecurityFilter securityFilter) {
        final String filterName = getFilterName(securityFilterServiceReference, bundleContext);

        securityFilter.init();
        keysOfInitializedSecurityFilters.add(getFilterKey(securityFilterServiceReference, bundleContext));
        LOGGER.debug("Initialized SecurityFilter {}", filterName);
    }

    public void removeSecurityFilter(final ServiceReference<SecurityFilter> securityFilterServiceReference) {
        if (securityFilterServiceReference != null) {
            final BundleContext bundleContext = getContext();
            if (bundleContext != null) {
                // unmark the SecurityFilter as initialized so that it can be re-initialized if the
                // SecurityFilter is registered again
                keysOfInitializedSecurityFilters
                        .remove(getFilterKey(securityFilterServiceReference, bundleContext));
                bundleContext.getService(securityFilterServiceReference).destroy();
            } else {
                LOGGER.warn(
                        "Unable to remove SecurityFilter. Try restarting the system or turning up logging to monitor current SecurityFilters.");
            }
        }
    }

    @Override
    public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory,
            Authentication.User validatedUser) {
        return true;
    }

    @Nonnull
    private static String getFilterKey(final ServiceReference<SecurityFilter> securityFilterServiceReference,
            final BundleContext bundleContext) {
        return getFilterName(securityFilterServiceReference, bundleContext);
    }

    /**
     * This logic to get the filter name from a {@link ServiceReference<Filter>} is copied from {@link
     * org.ops4j.pax.web.extender.whiteboard.internal.tracker.ServletTracker#createWebElement(ServiceReference,
     * javax.servlet.Servlet)}. See the pax-web Whiteboard documentation and {@link
     * org.osgi.service.http.whiteboard.HttpWhiteboardConstants#HTTP_WHITEBOARD_FILTER_NAME} for how
     * to configure {@link Filter} services with a filter name.
     */
    @Nonnull
    private static String getFilterName(ServiceReference<SecurityFilter> securityFilterServiceReference,
            BundleContext bundleContext) {
        final String HTTP_WHITEBOARD_FILTER_NAME = "osgi.http.whiteboard.filter.name";
        final String filterNameFromTheServiceProperty = getStringProperty(securityFilterServiceReference,
                HTTP_WHITEBOARD_FILTER_NAME);
        // If this service property is not specified, the fully qualified name of the service object's
        // class is used as the servlet filter name.
        if (StringUtils.isBlank(filterNameFromTheServiceProperty)) {
            return bundleContext.getService(securityFilterServiceReference).getClass().getCanonicalName();
        } else {
            return filterNameFromTheServiceProperty;
        }
    }

    private static String getStringProperty(ServiceReference<?> serviceReference, String key) {
        Object value = serviceReference.getProperty(key);
        if (value != null && !(value instanceof String)) {
            LOGGER.warn("Service property [key={}] value must be a String", key);
            return null;
        } else {
            return (String) value;
        }
    }

    private class DummyLoginService implements org.eclipse.jetty.security.LoginService {

        private final JettyIdentityService jettyIdentityService = new JettyIdentityService();

        @Override
        public String getName() {
            return null;
        }

        @Override
        public UserIdentity login(String username, Object credentials, ServletRequest request) {
            return null;
        }

        @Override
        public boolean validate(UserIdentity user) {
            return false;
        }

        @Override
        public IdentityService getIdentityService() {
            return jettyIdentityService;
        }

        @Override
        public void setIdentityService(IdentityService service) {
            // not needed
        }

        @Override
        public void logout(UserIdentity user) {
            // not needed
        }
    }
}