/*
* Copyright 2011 Federal Chancellery Austria and
* Graz University of Technology
*
* 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 at.gv.util.filter.moaid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import at.gv.util.MiscUtil;
import at.gv.util.ToStringUtil;
import at.gv.util.WebAppUtil;
/**
* @author Arne Tauber
* @author Thomas Knall
*/
public class MOAIDAuthenticationFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(MOAIDAuthenticationFilter.class);
public static final String USER_AUTH_DATA_ID = "AnyAuthDataObject:authenticatedUser";
public static final String STORED_REQUEST_URL_ID = String.class.getName() + ":" + "storedRequestURL";
public static final String WEB_XML_INIT_PARAM_LOGIN_PAGE = "loginPage";
public static final String WEB_XML_INIT_PARAM_ENABLED = "enabled";
public static final String WEB_XML_INIT_PARAM_ERROR_PAGE = "errorPage";
public static final String WEB_XML_INIT_PARAM_AUTHENTICATED_PAGE = "authenticatedPage"; // optional
public static final String WEB_XML_INIT_PARAM_SESSION_LOST_PAGE = "sessionLostPage"; // optional
public static final String WEB_XML_INIT_PARAM_ALLOWED_LIST = "allowedList";
public static final String WEB_XML_INIT_PARAM_ALLOWED_REGEX = "allowed";
private static final String WEB_XML_INIT_PARAM_EXCLUDED_PAGES_DELIMITER = ",";
private boolean enabled = true;
private static String loginPage = null;
private boolean loginPageForward = true;
private static String errorPage = null;
private static String authenticatedPage = null;
private static String sessionLostPage = null;
private static String[] excludedPages = null;
private static Pattern excludedRegEx = null;
private HttpServletRequest servletRequest = null;
public void destroy() {
log.trace("Shutting down " + this.getClass().getName() + "...");
}
public static String getErrorPage() {
return errorPage;
}
public static String getAuthenticatedPage() {
return authenticatedPage;
}
public static String getLoginPage() {
return loginPage;
}
public static String getSessionLostPage() {
return sessionLostPage;
}
public HttpServletRequest getHttpServletRequest() {
return this.servletRequest;
}
private boolean isExcluded(String url) {
boolean excluded = false;
if (MiscUtil.isNotEmpty(excludedPages)) {
for (String candidate : excludedPages) {
if (StringUtils.upperCase(url).endsWith(StringUtils.upperCase(candidate))) {
excluded = true;
break;
}
}
}
if (excludedRegEx != null && !excluded) {
// log.debug("Trying to match regex \"{}\" with \"{}\".",
// excludedRegEx.toString(), url);
if (excludedRegEx.matcher(url).matches()) {
excluded = true;
}
}
log.debug("URL \"" + url + "\" is " + (excluded ? "" : "NOT ") + "excluded from filter.");
return excluded;
}
public void doFilter(ServletRequest request, ServletResponse response, final FilterChain filterChain) throws IOException, ServletException {
if (this.enabled) {
log.debug("Applying " + this.getClass().getSimpleName() + "...");
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
servletRequest = httpServletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
HttpSession session = httpServletRequest.getSession();
log.debug("Using session " + session.getId() + ", created at " + DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(session.getCreationTime()) + ".");
Object anyObject = session.getAttribute(USER_AUTH_DATA_ID);
String requestURL = WebAppUtil.getRequestURLWithParameters(httpServletRequest, true);
log.trace("Request URL: " + requestURL);
if (anyObject == null && !this.isExcluded(requestURL)) {
Object dummyAuthData = this.provideDummyAuthenticationData();
if (dummyAuthData != null) {
log.warn("Unable to find regular authentication data but dummy authentication data is provided.");
log.debug("Authentication data = " + dummyAuthData.toString());
log.warn("Putting dummy authentication data into session.");
session.setAttribute(USER_AUTH_DATA_ID, dummyAuthData);
if (MiscUtil.isNotEmpty(getAuthenticatedPage())) {
if (loginPageForward) {
log.debug("Authenticated page is set. Forwarding to \"" + getAuthenticatedPage() + "\".");
RequestDispatcher dispatcher = request.getRequestDispatcher(getAuthenticatedPage());
dispatcher.forward(httpServletRequest, httpServletResponse);
} else {
log.debug("Authenticated page is set. Redirecting to \"" + getAuthenticatedPage() + "\".");
httpServletResponse.sendRedirect(httpServletResponse.encodeRedirectURL(getAuthenticatedPage()));
}
return;
}
} else {
if (MiscUtil.isNotEmpty(getAuthenticatedPage())) {
log.debug("Unable to find authentication data. Authenticated page is given so there is no need to save original request url. " + (loginPageForward ? "Forwarding" : "Redirecting") + " to login page \"" + loginPage + "\".");
} else {
log.debug("Unable to find authentication data. Storing request url and " + (loginPageForward ? "forwarding" : "redirecting") + " to login page \"" + loginPage + "\".");
// TODO: save HttpServletRequest
// log.debug("new CustomHttpServletRequest(request).toString() =
// {}", new
// CustomHttpServletRequest(httpServletRequest).toString());
session.setAttribute(STORED_REQUEST_URL_ID, requestURL);
}
String page = loginPage;
if (loginPage.startsWith("/") && !loginPage.startsWith("//"))
page = httpServletRequest.getContextPath() + loginPage;
if (loginPageForward) {
RequestDispatcher dispatcher = request.getRequestDispatcher(page);
dispatcher.forward(httpServletRequest, httpServletResponse);
} else {
httpServletResponse.sendRedirect(httpServletResponse.encodeRedirectURL(page));
}
return;
}
}
}
filterChain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
log.debug("Starting init of " + this.getClass().getName() + ".");
// enabled?
String enabledValue = StringUtils.trimToNull(filterConfig.getInitParameter(WEB_XML_INIT_PARAM_ENABLED));
this.enabled = BooleanUtils.isNotFalse(BooleanUtils.toBooleanObject(enabledValue));
// login page
loginPage = StringUtils.trim(filterConfig.getInitParameter(WEB_XML_INIT_PARAM_LOGIN_PAGE));
if (MiscUtil.isEmpty(loginPage)) {
throw new ServletException("ServletInitParameter \"" + WEB_XML_INIT_PARAM_LOGIN_PAGE + "\" must not be empty.");
}
loginPageForward = false; //!WebAppUtil.isFullQualifiedURL(loginPage);
// error page
errorPage = StringUtils.trim(filterConfig.getInitParameter(WEB_XML_INIT_PARAM_ERROR_PAGE));
if (MiscUtil.isEmpty(errorPage)) {
throw new ServletException("ServletInitParameter \"" + WEB_XML_INIT_PARAM_ERROR_PAGE + "\" must not be empty.");
}
// session lost page
sessionLostPage = StringUtils.trim(filterConfig.getInitParameter(WEB_XML_INIT_PARAM_SESSION_LOST_PAGE));
if (MiscUtil.isEmpty(sessionLostPage)) {
log.warn("ServletInitParameter \"" + WEB_XML_INIT_PARAM_SESSION_LOST_PAGE
+ "\" is empty. This parameter defines a failsafe url the browser is redirected to if the original url has been lost due to session timeout.");
}
// authenticated page
authenticatedPage = StringUtils.trim(filterConfig.getInitParameter(WEB_XML_INIT_PARAM_AUTHENTICATED_PAGE));
if (MiscUtil.isEmpty(authenticatedPage)) {
log.debug("ServletInitParameter \"" + WEB_XML_INIT_PARAM_AUTHENTICATED_PAGE
+ "\" is empty. This parameter defines the url the user is redirected to (instead of the original url) on successful authentication.");
}
String excluded = filterConfig.getInitParameter(WEB_XML_INIT_PARAM_ALLOWED_LIST);
ArrayList excludedList = new ArrayList();
if (MiscUtil.isNotEmpty(excluded)) {
StringTokenizer tokenizer = new StringTokenizer(excluded, WEB_XML_INIT_PARAM_EXCLUDED_PAGES_DELIMITER);
while (tokenizer.hasMoreTokens()) {
String ex = StringUtils.trim(tokenizer.nextToken());
if (MiscUtil.isNotEmpty(ex)) {
excludedList.add(ex);
}
}
}
excludedList.add(loginPage);
excludedList.add(errorPage);
excludedPages = new String[excludedList.size()];
excludedPages = excludedList.toArray(excludedPages);
String excludedRegExString = StringUtils.trim(filterConfig.getInitParameter(WEB_XML_INIT_PARAM_ALLOWED_REGEX));
if (MiscUtil.isNotEmpty(excludedRegExString)) {
excludedRegEx = Pattern.compile(excludedRegExString);
}
if (!this.enabled) {
log.info(this.getClass().getName() + " is DISABLED");
} else {
log.debug(WEB_XML_INIT_PARAM_ENABLED + " = \"" + this.enabled + "\"");
}
log.debug(WEB_XML_INIT_PARAM_LOGIN_PAGE + " [" + (loginPageForward ? "forward" : "redirect") + "] = \"" + loginPage + "\"");
log.debug(WEB_XML_INIT_PARAM_AUTHENTICATED_PAGE + " = \"" + (MiscUtil.isNotEmpty(authenticatedPage) ? authenticatedPage : "") + "\"");
log.debug(WEB_XML_INIT_PARAM_ERROR_PAGE + " = \"" + errorPage + "\"");
log.debug(WEB_XML_INIT_PARAM_SESSION_LOST_PAGE + " = \"" + (MiscUtil.isNotEmpty(sessionLostPage) ? sessionLostPage : "") + "\"");
log.debug(WEB_XML_INIT_PARAM_ALLOWED_LIST + " = " + ToStringUtil.toString(excludedPages, ", ", "\""));
log.debug(WEB_XML_INIT_PARAM_ALLOWED_REGEX + " = \"" + (excludedRegEx != null ? excludedRegEx.pattern() : "") + "\"");
}
/**
* May be overwritten in order to provide static authentication data during
* development process.
*
* @return some kind of dummy authentication data
*/
public Object provideDummyAuthenticationData() {
return null;
}
}