/*
* $Id: IWAuthenticator.java,v 1.39 2008/11/17 08:40:07 laddi Exp $ Created on
* 31.7.2004 in project com.idega.core
*
* Copyright (C) 2004-2005 Idega Software hf. All Rights Reserved.
*
* This software is the proprietary information of Idega hf. Use is subject to
* license terms.
*/
package com.idega.servlet.filter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.idega.business.IBOLookup;
import com.idega.business.IBOLookupException;
import com.idega.core.accesscontrol.business.AuthenticationBusiness;
import com.idega.core.accesscontrol.business.LoggedOnInfo;
import com.idega.core.accesscontrol.business.LoginBusinessBean;
import com.idega.core.accesscontrol.business.NotLoggedOnException;
import com.idega.core.accesscontrol.business.ServletFilterChainInterruptException;
import com.idega.core.accesscontrol.jaas.IWCallbackHandler;
import com.idega.core.accesscontrol.jaas.IWJAASAuthenticationRequestWrapper;
import com.idega.core.builder.business.BuilderService;
import com.idega.core.builder.business.BuilderServiceFactory;
import com.idega.idegaweb.IWApplicationContext;
import com.idega.idegaweb.IWException;
import com.idega.idegaweb.IWMainApplication;
import com.idega.idegaweb.IWMainApplicationSettings;
import com.idega.presentation.IWContext;
import com.idega.repository.data.ImplementorRepository;
import com.idega.user.business.UserBusiness;
import com.idega.user.data.User;
import com.idega.util.CoreConstants;
import com.idega.util.CypherText;
import com.idega.util.RequestUtil;
/**
* <p>
* This servletFilter is by default mapped early in the filter chain in idegaWeb
* and calls the idegaWeb Accesscontrol system to log the user in to the
* idegaWeb User system.<br/> When the user has a "remember me" cookie set then
* this filter reads that and logs the user into the system.
* </p>
* Last modified: $Date: 2008/11/17 08:40:07 $ by $Author: laddi $
*
* @author <a href="mailto:tryggvil@idega.com">Tryggvi Larusson</a>
* @version $Revision: 1.39 $
*/
public class IWAuthenticator extends BaseFilter {
public static final String PROPERTY_FORWARD_PAGE_URI = "FORWARD_PAGE_URI";
/**
* This parameter can be set
*/
public static final String PARAMETER_REDIRECT_USER_TO_PRIMARY_GROUP_HOME_PAGE = "logon_redirect_user";
/**
* This parameter can be set to forward to a certain page when logging in (and
* it is succesful)
*/
public static final String PARAMETER_REDIRECT_URI_ONLOGON = "logon_redirect_uri";
private static final String PERSONAL_ID_PATTERN = "\\$\\{currentUser.personalID\\}";
private static final String TICKET_PATTERN = "\\$\\{currentUser.ticket\\}";
/**
* This parameter can be set to forward to a certain page when logging off
* (and it is succesful)
*/
public static final String PARAMETER_REDIRECT_URI_ONLOGOFF = "logoff_redirect_uri";
/**
* This parameter can be set to forward to a certain page when logging in fails
*/
public static final String PARAMETER_REDIRECT_URI_ONLOGON_FAILED = "logon_failed_redirect_uri";
public static final String COOKIE_NAME = "iwrbusid";
//public String IW_BUNDLE_IDENTIFIER = "com.idega.block.login";
public static final String PARAMETER_ALLOWS_COOKIE_LOGIN = "icusallows";
// following string are used as keys in the sharedState map used by LoginModules
public static final String SESSION_KEY = "session"; // a HttpSession
public static final String REQUEST_KEY = "request"; // a HttpRequest
private static Logger log = Logger.getLogger(IWAuthenticator.class.getName());
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig arg0) throws ServletException {
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) srequest;
HttpServletResponse response = (HttpServletResponse) sresponse;
HttpSession session = request.getSession();
User lastLoggedOnAsUser = null;
LoginBusinessBean loginBusiness = getLoginBusiness(request);
boolean isLoggedOn = loginBusiness.isLoggedOn(request);
//if ((isLoggedOn && loginBusiness.isLogOffAction(request)) || loginBusiness.isLogOnAction(request) || loginBusiness.isTryAgainAction(request)) {
if (isLoggedOn) {
lastLoggedOnAsUser = loginBusiness.getCurrentUser(session);
}
if (useBasicAuthenticationMethod(request)) {
if (!isLoggedOn) {
if (!loginBusiness.authenticateBasicAuthenticationRequest(request)) {
loginBusiness.callForBasicAuthentication(request, response, null);
return;
}
}
}
else {
if (!isLoggedOn) {
loginBusiness.authenticateBasicAuthenticationRequest(request);
}
tryRegularLogin(request);
tryCookieLogin(request, response, loginBusiness);
}
processJAASLogin(request);
if (!isLoggedOn){
isLoggedOn = loginBusiness.isLoggedOn(request);
}
if (lastLoggedOnAsUser == null && isLoggedOn){
lastLoggedOnAsUser = loginBusiness.getCurrentUser(session);
}
boolean didInterrupt = processAuthenticationListeners(request, response, session, lastLoggedOnAsUser, loginBusiness, isLoggedOn);
if (didInterrupt) {
return;
}
boolean didRedirect = processRedirects(request, response, session, loginBusiness);
if (didRedirect) {
return;
}
chain.doFilter(new IWJAASAuthenticationRequestWrapper(request), response);
/*}
else {
chain.doFilter(request, response);
}*/
}
/**
* <p>
* Processes the possibly attatched AuthenticationListeners. Returns true if
* one of the authenticationfilters interrupts the filter chain.
* </p>
*
* @param request
* @param response
* @param session
* @param lastLoggedOnAsUser
* @param loginBusiness
* @param isLoggedOn
* @throws RemoteException
*/
protected boolean processAuthenticationListeners(HttpServletRequest request, HttpServletResponse response, HttpSession session, User lastLoggedOnAsUser, LoginBusinessBean loginBusiness, boolean isLoggedOn) throws RemoteException {
//TODO support also on basic authentication (e.g. webdav) or is that not necessery?
//TODO grab an interrupt exeption and just return; (could be necessery for the methods to be able to use response.sendRedirect)
if (loginBusiness.isLogOnAction(request) && isLoggedOn) {
try {
AuthenticationBusiness authenticationBusiness = getAuthenticationBusiness(request);
User currentUser = loginBusiness.getCurrentUser(session);
IWContext iwc = getIWContext(request, response);
authenticationBusiness.callOnLogonMethodInAllAuthenticationListeners(iwc, currentUser);
}
catch (ServletFilterChainInterruptException e) {
//this is normal behaviour if e.g. the listener issues a response.sendRedirect(...)
System.out.println("[IWAuthenticator] - Filter chain interrupted. The reason was: " + e.getMessage());
return true;
}
}
// else if(loginBusiness.isLogOffAction(request) && !isLoggedOn && lastLoggedOnAsUser!=null){
else if (loginBusiness.isLogOffAction(request) && lastLoggedOnAsUser != null) {
try {
AuthenticationBusiness authenticationBusiness = getAuthenticationBusiness(request);
//TODO: Remove IWContext
IWContext iwc = new IWContext(request, response, request.getSession().getServletContext());
authenticationBusiness.callOnLogoffMethodInAllAuthenticationListeners(iwc, lastLoggedOnAsUser);
}
catch (ServletFilterChainInterruptException e) {
//this is normal behaviour if e.g. the listener issues a response.sendRedirect(...)
System.out.println("[IWAuthenticator] - Filter chain interrupted. The reason was: " + e.getMessage());
return true;
}
}
return false;
}
/**
* <p>
* Processes possible redirects that might happen at login time. Returns true
* if a redirect did happen.
* </p>
*
* @param request
* @param response
* @param session
* @param loginBusiness
* @return
* @throws IOException
* @throws RemoteException
*/
protected boolean processRedirects(HttpServletRequest request, HttpServletResponse response, HttpSession session, LoginBusinessBean loginBusiness) throws IOException, RemoteException {
boolean isLoggedOn = loginBusiness.isLoggedOn(request);
boolean redirectToUserHomePage = false;
if (RequestUtil.isParameterSet(request, PARAMETER_REDIRECT_USER_TO_PRIMARY_GROUP_HOME_PAGE) && isLoggedOn) {
redirectToUserHomePage = true;
}
return processRedirects(request, response, session, loginBusiness, redirectToUserHomePage);
}
/**
* <p>
* Processes possible redirects that might happen at login time. Returns true
* if a redirect did happen.
* </p>
*
* @param request
* @param response
* @param session
* @param loginBusiness
* @return
* @throws IOException
* @throws RemoteException
*/
protected boolean processRedirects(HttpServletRequest request, HttpServletResponse response, HttpSession session, LoginBusinessBean loginBusiness, boolean redirectToUserHomePage) throws IOException, RemoteException {
//We have to call this method again because the user might just have logged on before:
boolean isLoggedOn = loginBusiness.isLoggedOn(request);
if (redirectToUserHomePage) {
return redirectToUserHomepage(request, response, loginBusiness);
}
if (RequestUtil.isParameterSet(request, PARAMETER_REDIRECT_URI_ONLOGON) && isLoggedOn) {
String uriToParse = request.getParameter(PARAMETER_REDIRECT_URI_ONLOGON);
String uri = getLoginRedirectUriOnLogonParsedWithVariables(request,uriToParse);
if (uri != null) {
response.sendRedirect(uri);
return true;
}
}
if (RequestUtil.isParameterSet(request, PARAMETER_REDIRECT_URI_ONLOGOFF) && !isLoggedOn) {
String uri = request.getParameter(PARAMETER_REDIRECT_URI_ONLOGOFF);
if (uri != null) {
response.sendRedirect(uri);
return true;
}
}
if (RequestUtil.isParameterSet(request, PARAMETER_REDIRECT_URI_ONLOGON_FAILED) && !isLoggedOn) {
String uriToParse = request.getParameter(PARAMETER_REDIRECT_URI_ONLOGON_FAILED);
String uri = getLoginRedirectUriOnLogonParsedWithVariables(request,uriToParse);
if (uri != null) {
response.sendRedirect(uri);
return true;
}
}
return false;
}
/**
* @see com.idega.user.business.UserBusinessBean#getHomePageIDForUser(User)
*
* @param request
* @param response
* @param isLoggedOn
* @throws IOException
* @throws RemoteException
*/
protected boolean redirectToUserHomepage(HttpServletRequest request, HttpServletResponse response, LoginBusinessBean loginBusiness) throws IOException, RemoteException {
HttpSession session = request.getSession();
User user = loginBusiness.getCurrentUser(session);
IWMainApplication iwMainApplication = getIWMainApplication(request);
IWApplicationContext iwac = iwMainApplication.getIWApplicationContext();
UserBusiness userBusiness = (UserBusiness) IBOLookup.getServiceInstance(iwac, UserBusiness.class);
int redirectPageId = userBusiness.getHomePageIDForUser(user);
if (redirectPageId > 0) {
response.sendRedirect(getBuilderService(iwac).getPageURI(redirectPageId));
return true;
}
log.log(Level.INFO, "Didn't find user's " + user.getName() + " home page");
return false;
}
/**
* <p>
* Parses the set RedirectOnLogon URI and replaces with user variables such as
* the variables ${currentUser.personalID}, ${currentUser.ticket} in the URL
* String.
* </p>
*
* @param request
* @return
*/
public static String getLoginRedirectUriOnLogonParsedWithVariables(HttpServletRequest request, String uri) {
if (uri != null) {
try {
uri = URLDecoder.decode(uri, CoreConstants.ENCODING_UTF8);
List<String> ignoreParams = new ArrayList<String>();
ignoreParams.add(PARAMETER_REDIRECT_URI_ONLOGON);
ignoreParams.add(PARAMETER_REDIRECT_URI_ONLOGON_FAILED);
ignoreParams.add(LoginBusinessBean.PARAMETER_USERNAME);
ignoreParams.add(LoginBusinessBean.PARAMETER_PASSWORD);
ignoreParams.add(LoginBusinessBean.PARAMETER_PASSWORD2);//whatever that is...
ignoreParams.add(LoginBusinessBean.LoginStateParameter);
ignoreParams.add(LoginBusinessBean.PARAM_LOGIN_BY_UNIQUE_ID);
String parameterString = RequestUtil.getParametersStringFromRequest(request,ignoreParams);
if (parameterString.length() > 0) {
uri += (uri.indexOf("?") == -1 ? "?" : "&") + parameterString;
}
}
catch (UnsupportedEncodingException e) {
log.log(Level.WARNING, "Exception while decoding redirect uri parameter: " + PARAMETER_REDIRECT_URI_ONLOGON + " by using " + CoreConstants.ENCODING_UTF8 + " encoding", e);
}
}
uri = getUriParsedWithVariables(request, uri);
return uri;
}
/**
* <p>
* Parses the set RedirectOnLogon URI and replaces with user variables such as
* the variables ${currentUser.personalID}, ${currentUser.ticket} in the URL
* String.
* </p>
*
* @param request
* @return
*/
public static String getUriParsedWithVariables(HttpServletRequest request, String uri) {
LoginBusinessBean loginBean = LoginBusinessBean.getLoginBusinessBean(request);
LoggedOnInfo info = loginBean.getLoggedOnInfo(request.getSession());
User user = null;
try{
user = loginBean.getCurrentUser(request.getSession());
if (user != null) {
String personalId = user.getPersonalID();
uri = uri.replaceAll(PERSONAL_ID_PATTERN, personalId);
}
} catch (NotLoggedOnException e){
//Do nothing
}
if(info != null){
String ticket = info.getTicket();
if (ticket != null) {
uri = uri.replaceAll(TICKET_PATTERN, ticket);
}
}
return uri;
}
/**
* @param iwc
* @return
*/
private boolean useBasicAuthenticationMethod(HttpServletRequest request) {
return IWContext.isWebDavClient(request);
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#destroy()
*/
public void destroy() {
}
public void tryRegularLogin(HttpServletRequest request) {
try {
getLoginBusiness(request).processRequest(request);
}
catch (IWException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* <p>
* Authenticates in/out with reading existing cookie or creating it on login
* or removing it on logout
* </p>
*
* @param request
* @param response
* @param loginBusiness
*/
public void tryCookieLogin(HttpServletRequest request, HttpServletResponse response, LoginBusinessBean loginBusiness) {
if (!loginBusiness.isLoggedOn(request)) {
//No user is logged in, try to authenticate with the cookie:
Cookie userIDCookie = getCookie(request);
//System.err.println("no user is logged on");
if (userIDCookie != null) {
//System.err.println("found the cookie");
if (loginBusiness.isLogOffAction(request)) {
//A cookie is found
//delete it:
userIDCookie.setMaxAge(0);
response.addCookie(userIDCookie);
}
else {
String cypheredLoginName = userIDCookie.getValue();
IWApplicationContext iwac = getIWMainApplication(request).getIWApplicationContext();
String loginName = deCypherUserLogin(iwac, cypheredLoginName);
try {
loginBusiness.logInUnVerified(request, loginName);
}
catch (Exception ex) {
//throw new IWException("Cookie login failed :
// "+ex.getMessage());
log.warning("Cookie login failed for loginName: " + loginName + " :" + ex.getMessage());
}
}
}
else {
}
}
else {
//A user is logged in
if (loginBusiness.isLogOffAction(request)) {
Cookie userIDCookie = getCookie(request);
if (userIDCookie != null) {
//A cookie is found, try to remove it on logout
userIDCookie.setMaxAge(0);
response.addCookie(userIDCookie);
}
}
else if (RequestUtil.isParameterSet(request, PARAMETER_ALLOWS_COOKIE_LOGIN)) {
Cookie userIDCookie = getCookie(request);
HttpSession session = request.getSession();
String login = loginBusiness.getLoggedOnInfo(session).getLogin();
IWMainApplication iwma = getIWMainApplication(request);
IWApplicationContext iwac = iwma.getIWApplicationContext();
String cypheredLogin = cypherUserLogin(iwac, login);
int maxAge = 60 * 60 * 24 * 30;
if (userIDCookie == null) {
//No cookie exists on logon, try to add it:
userIDCookie = new Cookie(COOKIE_NAME, cypheredLogin);
userIDCookie.setMaxAge(maxAge);
response.addCookie(userIDCookie);
}
else {
userIDCookie.setValue(login);
userIDCookie.setMaxAge(maxAge);
}
}
}
}
private Cookie getCookie(HttpServletRequest request) {
Cookie userIDCookie = RequestUtil.getCookie(request, COOKIE_NAME);
return userIDCookie;
}
public String getCypherKey(IWApplicationContext iwc) {
CypherText cyph = new CypherText();
IWMainApplicationSettings settings = iwc.getApplicationSettings();
String cypherKey = settings.getProperty("cypherKey");
if ((cypherKey == null) || (cypherKey.equalsIgnoreCase(""))) {
cypherKey = cyph.getKey(100);
settings.setProperty("cypherKey", cypherKey);
}
return (cypherKey);
}
protected String cypherUserLogin(IWApplicationContext iwc, String userLogin) {
String key = getCypherKey(iwc);
String cypheredId = new CypherText().doCyper(userLogin, key);
log.fine("Cyphered " + userLogin + "to " + cypheredId);
return cypheredId;
}
protected String deCypherUserLogin(IWApplicationContext iwc, String cypheredLogin) {
String key = getCypherKey(iwc);
return new CypherText().doDeCypher(cypheredLogin, key);
}
protected AuthenticationBusiness getAuthenticationBusiness(HttpServletRequest request) {
AuthenticationBusiness authenticationBusiness = null;
try {
IWApplicationContext iwac = getIWMainApplication(request).getIWApplicationContext();
authenticationBusiness = (AuthenticationBusiness) IBOLookup.getServiceInstance(iwac, AuthenticationBusiness.class);
}
catch (IBOLookupException e) {
e.printStackTrace();
}
return authenticationBusiness;
}
protected BuilderService getBuilderService(IWApplicationContext iwac) throws RemoteException {
return BuilderServiceFactory.getBuilderService(iwac);
}
@SuppressWarnings("unchecked")
protected void processJAASLogin(HttpServletRequest request) {
List loginModules = ImplementorRepository.getInstance().newInstances(LoginModule.class, this.getClass());
// just a shortcut
if (loginModules.isEmpty()) {
return;
}
CallbackHandler callbackHandler = new IWCallbackHandler(request);
Map sharedState = new HashMap(3);
HttpSession session = request.getSession();
sharedState.put(IWAuthenticator.REQUEST_KEY, request);
sharedState.put(IWAuthenticator.SESSION_KEY, session);
Iterator iteratorFirst = loginModules.iterator();
while (iteratorFirst.hasNext()) {
LoginModule loginModule = (LoginModule) iteratorFirst.next();
try {
loginModule.initialize(null, callbackHandler, sharedState, null);
loginModule.login();
}
catch (LoginException e) {
e.printStackTrace();
}
}
Iterator iteratorSecond = loginModules.iterator();
while (iteratorSecond.hasNext()) {
LoginModule loginModule = (LoginModule) iteratorSecond.next();
try {
loginModule.commit();
}
catch (LoginException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
String encoded = URLEncoder.encode("http://formbuilder.idega.is/login/?logon_redirect_uri=/workspace/", CoreConstants.ENCODING_UTF8);
System.out.println("encooded: " + encoded);
String decoded = URLDecoder.decode(encoded, CoreConstants.ENCODING_UTF8);
System.out.println("decoded: " + decoded);
}
catch (Exception e) {
e.printStackTrace();
}
}
}