/*
* Copyright (c) 2009 Lockheed Martin Corporation
*
* 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 org.eurekastreams.server.service.security.preauth;
import java.io.IOException;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
import org.springframework.security.AuthenticationException;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.util.RedirectUtils;
/**
* <p>
* In the pre-authenticated authentication case (unlike CAS, for example) the
* user will already have been identified through some external mechanism and a
* secure context established by the time the security-enforcement filter is
* invoked.
* <p>
* Therefore this class isn't actually responsible for the commencement of
* authentication, as it is in the case of other providers. It will be called if
* the user is rejected by the AbstractPreAuthenticatedProcessingFilter,
* resulting in a null authentication.
* <p>
* Depending on the Exception The <code>commence</code> method will forward to
* configured value, or if a match is not found return an
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).
* <p>
* To configure this filter to redirect to specific pages as the result of
* specific {@link AuthenticationException}s you can do the following.
* Configure the <code>exceptionMappings</code> property in your application
* xml. This property is a java.util.Properties object that maps a
* fully-qualified exception class name to a redirection url target. For
* example:
*
* <pre>
* <property name="exceptionMappings">
* <props>
* <prop> key="org.springframework.security.DisabledException">/disabled.jsp</prop>
* </props>
* </property>
* </pre>
*
* The example above would redirect all
* {@link org.springframework.security.DisabledException}s thrown, to a page in the
* web-application called /disabled.jsp.
* <p>
* Any {@link AuthenticationException} thrown that cannot be matched in the
* <code>exceptionMappings</code> will be redirected to the
* <code>authenticationFailureUrl</code>
* <p>
*/
public class PreAuthenticationFilterEntryPoint implements AuthenticationEntryPoint, Ordered
{
/**
* Local log instance.
*/
private static Log logger = LogFactory.getLog(PreAuthenticationFilterEntryPoint.class);
/**
* Order of filter in filter chain.
*/
private int order = Integer.MAX_VALUE;
/**
* Exception mappings for determining where to go for specific exceptions.
*/
private Properties exceptionMappings = new Properties();
/**
* Tells if we are to do a server side include of the error URL instead of a 302 redirect.
*/
private boolean serverSideRedirect = false;
/**
* If true, causes any redirection URLs to be calculated minus the protocol
* and context path (defaults to false).
*/
private boolean useRelativeContext = false;
/**
* {@inheritDoc}
*/
public void commence(final ServletRequest inRequest, final ServletResponse inResponse,
final AuthenticationException inAuthenticationException) throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest) inRequest;
HttpServletResponse response = (HttpServletResponse) inResponse;
if (logger.isDebugEnabled())
{
logger.debug("Authentication request failed: " + inAuthenticationException.toString());
}
String failureUrl = determineFailureUrl(inAuthenticationException);
if (failureUrl == null)
{
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied:"
+ inAuthenticationException.getMessage());
}
else if (serverSideRedirect)
{
request.getRequestDispatcher(failureUrl).forward(request, response);
}
else
{
RedirectUtils.sendRedirect(request, response, failureUrl, useRelativeContext);
}
}
/**
* Return url to redirect to for a given {@link AuthenticationException}. If no mapping is present, null is
* returned.
*
* @param inAuthException
* The {@link AuthenticationException}.
* @return url to redirect to for a given {@link AuthenticationException}. If no mapping is present, null is
* returned.
*/
protected String determineFailureUrl(final AuthenticationException inAuthException)
{
return exceptionMappings.getProperty(inAuthException.getClass().getName());
}
/**
* {@inheritDoc}
*/
public int getOrder()
{
return order;
}
/**
* Setter for filter order.
* @param inOrder
* the order to set
*/
public void setOrder(final int inOrder)
{
this.order = inOrder;
}
/**
* Set value for useRelativeContext.
* @param inUseRelativeContext If true, causes any redirection URLs to be calculated minus the protocol
* and context path (defaults to false).
*/
public void setUseRelativeContext(final boolean inUseRelativeContext)
{
this.useRelativeContext = inUseRelativeContext;
}
/**
* Get value for useRelativeContext.
* @return Value for useRelativeContext.
*/
public Properties getExceptionMappings()
{
return new Properties(exceptionMappings);
}
/**
* Set the exception mappings collection for this class.
* @param inExceptionMappings Exception mappings collection for this class.
*/
public void setExceptionMappings(final Properties inExceptionMappings)
{
this.exceptionMappings = inExceptionMappings;
}
/**
* Tells if we are to do a server side include of the error URL instead of a 302 redirect.
*
* @param inServerSideRedirect Value for serverSideRedirect.
*/
public void setServerSideRedirect(final boolean inServerSideRedirect)
{
this.serverSideRedirect = inServerSideRedirect;
}
}