DelegatingNegotiateSecurityFilter.java

/**
 * Waffle (https://github.com/dblock/waffle)
 *
 * Copyright (c) 2010 - 2015 Application Security, Inc.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Application Security, Inc.
 */
/**
 * 
 */
package waffle.spring;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

/**
 * 
 * 
 * <p>
 * Supports optional injection of spring security entities, allowing Waffle to act as an interface towards an identity
 * provider(the AD).
 * </p>
 * 
 * <i>Below mentioned entities are verified to be set before invoked, inherited entities are not.</i>
 * 
 * <ul>
 * <li>
 * The <code>AuthenticationManager</code> allows for the service provider to authorize the principal.</li>
 * 
 * <li>
 * The <code>authenticationSuccessHandler</code> allows for the service provider to further populate the
 * {@link org.springframework.security.core.Authentication Authentication} object.</li>
 * 
 * <li>
 * The <code>AuthenticationFailureHandler</code> is called if the AuthenticationManager throws an
 * {@link org.springframework.security.core.AuthenticationException AuthenticationException}.</li>
 * 
 * <li>
 * The <code>AccessDeniedHandler</code> is called if the AuthenticationManager throws an
 * {@link org.springframework.security.access.AccessDeniedException AccessDeniedException}.</li>
 * </ul>
 * Example configuration:
 * 
 * <pre>
 * {@code
 * <bean id="waffleNegotiateSecurityFilter"
 * 		class="waffle.spring.DelegatingNegotiateSecurityFilter"
 * 		scope="tenant">
 * 		<property name="allowGuestLogin" value="false" />
 * 		<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
 * 		<property name="authenticationManager" ref="authenticationManager" />
 * 		<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
 * 		<property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
 * 		<property name="accessDeniedHandler" ref="accessDeniedHandler" />
 * 		<property name="defaultGrantedAuthority">
 * 			<null />
 * 		</property>
 * 	</bean>
 * </code>
 * }
 * </pre>
 */
public class DelegatingNegotiateSecurityFilter extends NegotiateSecurityFilter {
    
    /** The Constant LOGGER. */
    private static final Logger          LOGGER = LoggerFactory.getLogger(NegotiateSecurityFilter.class);

    /** The authentication manager. */
    private AuthenticationManager        authenticationManager;
    
    /** The authentication success handler. */
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    
    /** The authentication failure handler. */
    private AuthenticationFailureHandler authenticationFailureHandler;
    
    /** The access denied handler. */
    private AccessDeniedHandler          accessDeniedHandler;

    /**
     * Gets the access denied handler.
     *
     * @return the accessDeniedHandler
     */
    public AccessDeniedHandler getAccessDeniedHandler() {
        return this.accessDeniedHandler;
    }

    /**
     * Sets the access denied handler.
     *
     * @param accessDeniedHandler
     *            the accessDeniedHandler to set
     */
    public void setAccessDeniedHandler(final AccessDeniedHandler accessDeniedHandler) {
        this.accessDeniedHandler = accessDeniedHandler;
    }

    /**
     * Gets the authentication failure handler.
     *
     * @return the authenticationFailureHandler
     */
    public AuthenticationFailureHandler getAuthenticationFailureHandler() {
        return this.authenticationFailureHandler;
    }

    /**
     * Sets the authentication failure handler.
     *
     * @param authenticationFailureHandler
     *            the authenticationFailureHandler to set
     */
    public void setAuthenticationFailureHandler(final AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }

    /**
     * Instantiates a new delegating negotiate security filter.
     */
    public DelegatingNegotiateSecurityFilter() {
        super();
        DelegatingNegotiateSecurityFilter.LOGGER.debug("[waffle.spring.NegotiateSecurityFilter] loaded");
    }

    /* (non-Javadoc)
     * @see waffle.spring.NegotiateSecurityFilter#setAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
     */
    @Override
    protected boolean setAuthentication(final HttpServletRequest request, final HttpServletResponse response,
            final Authentication authentication) {
        try {
            if (this.authenticationManager != null) {
                this.logger.debug("Delegating to custom authenticationmanager");
                final Authentication customAuthentication = this.authenticationManager.authenticate(authentication);
                SecurityContextHolder.getContext().setAuthentication(customAuthentication);
            }
            if (this.authenticationSuccessHandler != null) {
                try {
                    this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);
                } catch (final IOException e) {
                    this.logger.warn("Error calling authenticationSuccessHandler: " + e.getMessage());
                    return false;
                } catch (final ServletException e) {
                    this.logger.warn("Error calling authenticationSuccessHandler: " + e.getMessage());
                    return false;
                }
            }
        } catch (final AuthenticationException e) {

            this.logger.warn("Error authenticating user in custom authenticationmanager: " + e.getMessage());
            this.sendAuthenticationFailed(request, response, e);
            return false;
        } catch (final AccessDeniedException e) {
            this.logger.warn("Error authorizing user in custom authenticationmanager: " + e.getMessage());
            this.sendAccessDenied(request, response, e);
            return false;
        }
        return true;
    }

    /* (non-Javadoc)
     * @see waffle.spring.NegotiateSecurityFilter#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();

        if (this.getProvider() == null) {
            throw new ServletException("Missing NegotiateSecurityFilter.Provider");
        }
    }

    /**
     * Forward to authenticationFailureHandler.
     *
     * @param request
     *            the request
     * @param response
     *            HTTP Response
     * @param ae
     *            the ae
     */
    private void sendAuthenticationFailed(final HttpServletRequest request, final HttpServletResponse response,
            final AuthenticationException ae) {
        if (this.authenticationFailureHandler != null) {
            try {
                this.authenticationFailureHandler.onAuthenticationFailure(request, response, ae);
                return;
            } catch (final IOException e) {
                DelegatingNegotiateSecurityFilter.LOGGER.warn("IOException invoking authenticationFailureHandler: " + e.getMessage());
            } catch (final ServletException e) {
                DelegatingNegotiateSecurityFilter.LOGGER.warn("ServletException invoking authenticationFailureHandler: " + e.getMessage());
            }
        }
        super.sendUnauthorized(response, true);
    }

    /**
     * Forward to accessDeniedHandler.
     *
     * @param request
     *            the request
     * @param response
     *            HTTP Response
     * @param ae
     *            the ae
     */
    private void sendAccessDenied(final HttpServletRequest request, final HttpServletResponse response,
            final AccessDeniedException ae) {
        if (this.accessDeniedHandler != null) {
            try {
                this.accessDeniedHandler.handle(request, response, ae);
                return;
            } catch (final IOException e) {
                DelegatingNegotiateSecurityFilter.LOGGER.warn("IOException invoking accessDeniedHandler: " + e.getMessage());
            } catch (final ServletException e) {
                DelegatingNegotiateSecurityFilter.LOGGER.warn("ServletException invoking accessDeniedHandler: " + e.getMessage());
            }
        }
        // fallback
        this.sendUnauthorized(response, true);
    }

    /**
     * Gets the authentication success handler.
     *
     * @return the authenticationSuccessHandler
     */
    public AuthenticationSuccessHandler getAuthenticationSuccessHandler() {
        return this.authenticationSuccessHandler;
    }

    /**
     * Sets the authentication success handler.
     *
     * @param authenticationSuccessHandler
     *            the authenticationSuccessHandler to set
     */
    public void setAuthenticationSuccessHandler(final AuthenticationSuccessHandler authenticationSuccessHandler) {
        this.authenticationSuccessHandler = authenticationSuccessHandler;
    }

    /**
     * Gets the authentication manager.
     *
     * @return the authenticationManager
     */
    public AuthenticationManager getAuthenticationManager() {
        return this.authenticationManager;
    }

    /**
     * Sets the authentication manager.
     *
     * @param authenticationManager
     *            the authenticationManager to set
     */
    public void setAuthenticationManager(final AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

}