/**
* Copyright (C) 2008 Google - Enterprise EMEA SE
*
* 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 com.google.gsa.valve.rootAuth;
import java.io.IOException;
import java.net.URLDecoder;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.httpclient.HttpException;
import org.apache.log4j.Logger;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import com.google.gsa.AuthorizationProcessImpl;
import com.google.gsa.Credentials;
import com.google.gsa.sessions.Sessions;
import com.google.gsa.sessions.UserSession;
import com.google.gsa.valve.configuration.ValveConfiguration;
import com.google.gsa.valve.configuration.ValveRepositoryConfiguration;
import com.google.gsa.valve.utils.URLUTF8Encoder;
import com.google.gsa.sessions.nonValidSessionException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Vector;
/**
* This is the default class that drives the authorization process based on the
* repositories declared in the config file, where the individual authorization
* class is included there as well.
* <p>
* The name of the authentication classes that need to be processed are included
* in a vector that is reused multiple times to check if URL patterns with any
* of them. If there is any URL pattern defined in the config file that
* matches with the url sent to the authorize() method, a new authorization
* class of that kind is created.
* <p>
* At the end, it collects the error message coming from the specific
* authorization class' authorize() method. If there is any problem during
* the processing, it's returned as well.
*
* @see RootAuthenticationProcess
*
*/
public class RootAuthorizationProcess implements AuthorizationProcessImpl {
//logger
private Logger logger = null;
//Valve configuration
private ValveConfiguration valveConf = null;
private static Vector<ValveRepositoryConfiguration> repositoryConfigurations =
null;
//Krb and session vars
private static boolean isKerberos = false;
private static boolean isSessionEnabled = false;
private static boolean isSAML = false;
private static boolean sendCookies = true;
private static int sessionVarsSet = -1;
private static String authCookieName = null;
private static String internalURL = null;
private Sessions sessions = null;
private static final String ENCODING = "UTF-8";
/**
* Class constructor
*
*/
public RootAuthorizationProcess() {
// Invoke parent constructor
super();
logger = Logger.getLogger(RootAuthorizationProcess.class);
}
/**
* Sets user credentials
*
* @param creds
*/
public void setCredentials(Credentials creds) {
//do nothing
}
/**
*
* This is the default root authorize method that manages the whole
* authorization lifecycle when accessing the backend repositories.
* <p>
* Based on the information included in the config file, it uses that
* information to manage the authorization process. If there is any URL
* pattern defined in the config file that matches with the url sent to
* the authorize() method, a new authorization class of that kind is created.
* <p>
* At the end, it collects the error message coming from the specific
* authorization class' authorize() method. If there is any problem during
* the processing, it's returned as well.
*
* @param request HTTP request
* @param response HTTP response
* @param authCookies vector that contains the authentication cookies
* @param url the document url
* @param id the default credential id
*
* @return the HTTP error code
*
* @throws HttpException
* @throws IOException
*/
public int authorize(HttpServletRequest request,
HttpServletResponse response, Cookie[] authCookies,
String url, String id) throws HttpException,
IOException,
nonValidSessionException {
logger.debug("Authorize");
// Initialize status code
int statusCode = HttpServletResponse.SC_UNAUTHORIZED;
boolean patternMatch = false;
boolean rootIDExists = false;
//UserSession
UserSession userSession = null;
//GSA cookie
Cookie gsaAuthCookie = null;
//Encoding support
String newURL = null;
//Try to avoid the double encoding problem
try {
newURL = URLDecoder.decode(url, ENCODING);
} catch (IllegalArgumentException e) {
logger.error("Illegal Argument when decoding/encoding URL");
newURL = url;
}
URLUTF8Encoder encoder = new URLUTF8Encoder();
url = encoder.encodeURL(new URL(newURL));
//read vars
if (valveConf != null) {
//Set config vars
setValveConf();
} else {
logger.error("Configuration error: Config file is not present");
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Configuration error - Kerberos is not set properly");
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
//set auth Cookie
Cookie[] cookies = request.getCookies();
//SAML
if (cookies == null) {
cookies = authCookies;
}
if (cookies != null) {
logger.debug("authCookieName is: " + authCookieName);
for (int i = 0; i < cookies.length; i++) {
logger.debug("Cookie found: " + cookies[i].getName() +
"; value=" + cookies[i].getValue());
if (cookies[i].getName().equals(authCookieName)) {
gsaAuthCookie = cookies[i];
logger.debug("Auth Cookie found!");
break;
}
}
}
//manage Sessions
if (isSessionEnabled) {
logger.debug("Session is enabled. Getting session instance");
try {
//Session Support. Get Sessions instance
sessions = Sessions.getInstance();
//Get user session
userSession = manageSessions(gsaAuthCookie);
} catch (nonValidSessionException nVS) {
//throw Exception
throw nVS;
} catch (Exception e) {
logger.error("Error when geting session: " + e.getMessage(),
e);
}
}
//setting auth cookies
if ((!isSessionEnabled) ||
((isSessionEnabled) && (sendCookies) && (!isSAML))) {
//send auth cookies as those coming straight from the browser
authCookies = request.getCookies();
} else {
//auth cookies are those that are in the session
authCookies = userSession.getCookies();
}
logger.debug("Authz authorizing [" + url + "]");
//protection
if (repositoryConfigurations == null) {
logger.error("Authorization Repository Vector has not been initialized");
return HttpServletResponse.SC_UNAUTHORIZED;
}
//Pattern of the host that has been confiogured that needs to be macthed to the URL that is being authorized.
RE authZHost = null;
//The host of the GSA, need to detect a request from this host and skip past it
RE queryHostRE = null;
try {
queryHostRE = new RE("/search", RE.MATCH_CASEINDEPENDENT);
} catch (RESyntaxException reSynTaxExp) {
logger.error("Failed to created queryHost RE: " +
reSynTaxExp.getMessage());
}
ValveRepositoryConfiguration repository = null;
logger.debug("Repository length: " + repositoryConfigurations.size());
for (int i = 0; i < repositoryConfigurations.size(); i++) {
repository = repositoryConfigurations.elementAt(i);
logger.debug("Repository ID: " + repository.getId());
//Pattern for this repository that needs to be macthed
try {
authZHost =
new RE(repository.getPattern(), RE.MATCH_CASEINDEPENDENT);
} catch (RESyntaxException reSynTaxExp) {
logger.error("Failed to created authZHost RE: " +
reSynTaxExp.getMessage());
logger.error("Pattern trying to use: " +
repository.getPattern());
}
if (queryHostRE.match(url)) {
logger.debug("Query AuthZ");
statusCode = HttpServletResponse.SC_OK;
patternMatch = true;
} else {
if (authZHost.match(url)) {
//Need the correct authZProcess implementation for this repository ID
AuthorizationProcessImpl authZProcess =
getAuthorizationProcess(repository);
if (authZProcess != null) {
//URL matches a pattern
if (repository.getId().equals("root")) {
//If this is a match for the root id then it's the internal host used to test valve/test.html, so should just return valid
logger.debug("Internal AuthZ");
statusCode = HttpServletResponse.SC_OK;
patternMatch = true;
rootIDExists = true;
} else {
logger.info("Authorizing with " +
repository.getId());
patternMatch = true;
//Add credentials
try {
addCredentials(authZProcess, userSession);
} catch (Exception e) {
logger.error("Error during Kerberos authZ treatment : " +
e.getMessage(), e);
}
try {
String repoID = repository.getId();
statusCode =
authZProcess.authorize(request, response,
authCookies,
url, repoID);
//If statusCode is UNAUTHORIZED, then the process has to stop here
if (statusCode ==
HttpServletResponse.SC_UNAUTHORIZED) {
break;
}
} catch (Exception e) {
logger.error("Error during authorization: " +
e.getMessage(), e);
}
}
} else {
logger.debug("The URL matches with the pattern defined for repository " +
"[" + repository.getId() +
"] but could not instantiate the class");
}
}
}
}
if (!patternMatch) {
//check if "root" repository was created in the config file
//if not: check if the URL is a Valve one. If so, return SC_OK
if (!rootIDExists) {
RE internalRE =
new RE(new URL(internalURL).getHost(), RE.MATCH_CASEINDEPENDENT);
boolean samePorts =
(((new URL(internalURL)).getPort()) == ((new URL(url)).getPort()));
if ((internalRE.match(url)) && (samePorts)) {
logger.debug("This is an internal URL");
statusCode = HttpServletResponse.SC_OK;
} else {
logger.debug("No pattern has been defined at any repository for this URL");
//Set Status Code equal to "-1", so we do know there was no pattern found
statusCode = -1;
}
} else {
logger.debug("No pattern has been defined at any repository for this URL");
//Set Status Code equal to "-1", so we do know there was no pattern found
statusCode = -1;
}
}
//protection
userSession = null;
return statusCode;
}
/**
* Gets the authorization process instance needed to process the request
*
* @param repository the repository configuration information
*
* @return the authorization class
*/
private AuthorizationProcessImpl getAuthorizationProcess(ValveRepositoryConfiguration repository) {
AuthorizationProcessImpl authProcess = null;
//protection
if (repository != null) {
try {
String authZComponent = repository.getAuthZ();
logger.debug("Authorization module is: " + authZComponent);
if (authZComponent != null) {
authProcess =
(AuthorizationProcessImpl)Class.forName(authZComponent).newInstance();
authProcess.setValveConfiguration(valveConf);
} else {
logger.debug("This repository[" + repository.getId() +
"] does not cointain any Authorization class");
}
} catch (LinkageError le) {
logger.error(repository.getId() +
" - Can't instantiate class [AuthorizationProcess-LinkageError]: " +
le.getMessage(), le);
authProcess = null;
} catch (InstantiationException ie) {
logger.error(repository.getId() +
" - Can't instantiate class [AuthorizationProcess-InstantiationException]: " +
ie.getMessage(), ie);
authProcess = null;
} catch (IllegalAccessException iae) {
logger.error(repository.getId() +
" - Can't instantiate class [AuthorizationProcess-IllegalAccessException]: " +
iae.getMessage(), iae);
authProcess = null;
} catch (ClassNotFoundException cnfe) {
logger.error(repository.getId() +
" - Can't instantiate class [AuthorizationProcess-ClassNotFoundException]: " +
cnfe.getMessage(), cnfe);
authProcess = null;
} catch (Exception e) {
logger.error(repository.getId() +
" - Can't instantiate class [AuthorizationProcess-Exception]: " +
e.getMessage(), e);
authProcess = null;
}
}
return authProcess;
}
/**
* Sets the Valve Configuration instance to read the parameters
* from there
*
* @param valveConf the Valve configuration instance
*/
public void setValveConfiguration(ValveConfiguration valveConf) {
//if (this.valveConf == null) {
logger.debug("Setting Valve Configuration");
this.valveConf = valveConf;
if (repositoryConfigurations == null) {
setRepositoryConfigurations();
}
//}
}
/**
* Sets the vector that contains all the authorization class names
* in order to process it more efficiently.
*
*/
private void setRepositoryConfigurations() {
//Instantiate each of the repositories defined in the configuration
repositoryConfigurations = new Vector<ValveRepositoryConfiguration>();
String repositoryIds[] = valveConf.getRepositoryIds();
ValveRepositoryConfiguration repository = null;
logger.debug("Reading repositories");
for (int i = 0; i < repositoryIds.length; i++) {
try {
repository = valveConf.getRepository(repositoryIds[i]);
if (repository.getAuthZ() == null ||
repository.getAuthZ().equals("")) {
logger.info("No authZ defined for " + repository.getId());
} else {
logger.debug("Authorisation process for [" +
repository.getId() + "] found");
repositoryConfigurations.add(repository);
}
} catch (Exception e) {
logger.error("Error during Authorization Vector creation: " +
e.getMessage(), e);
}
}
logger.debug("Authorization vector has been created");
}
/**
* Sends the credentials store in the session to the backend application
*
* @param authZProcess authorization process instance
* @param userSession user session
*/
public void addCredentials(AuthorizationProcessImpl authZProcess,
UserSession userSession) {
logger.debug("addCredentials method");
try {
if (userSession != null) {
logger.debug("userSession is not empty: " +
userSession.getUserName());
Credentials credentials = userSession.getUserCredentials();
if (credentials != null) {
logger.debug("Setting credentials for authorization");
//Adding credentials
authZProcess.setCredentials(credentials);
} else {
logger.debug("There are no credentials available");
}
}
} catch (Exception ex) {
logger.error("Error when adding credentials: " + ex.getMessage(),
ex);
} finally {
}
}
/**
* It manages session and checks the session (if it exists) is still valid.
*
* @param gsaAuthCookie authentication cookie
*
* @return the user session if it exists, null otherwise
*
* @throws nonValidSessionException
*/
public UserSession manageSessions(Cookie gsaAuthCookie) throws nonValidSessionException {
UserSession userSession = null;
logger.debug("ManageSessions method. Check if Session is enabled [" +
isSessionEnabled + "]");
if (isSessionEnabled) {
//check if the session is active
logger.debug("The session is enabled");
String userID = null;
try {
userID = URLDecoder.decode(gsaAuthCookie.getValue(), ENCODING);
} catch (UnsupportedEncodingException e) {
logger.error("Error during decoding Auth Cookie: " +
e.getMessage(), e);
userID = gsaAuthCookie.getValue();
}
logger.debug("the userID has been read: " + userID);
boolean isSessionInvalid = sessions.isSessionInvalid(userID);
logger.debug("Session invalidity checked: " + isSessionInvalid);
if (isSessionInvalid) {
//protect this code
synchronized (sessions) {
logger.debug("the session is invalid");
boolean doesSessionStillExist =
sessions.doesSessionExist(userID);
logger.debug("Session still exists: " +
doesSessionStillExist);
if (doesSessionStillExist) {
logger.debug("the session does exists: let's delete it");
//delete Session
sessions.deleteSession(userID);
}
logger.debug("Setting session invalidity");
throw new nonValidSessionException("The session is invalid. It does not longer exists");
}
} //end session invalid
//look for the existing session
userSession = sessions.getUserSession(userID);
if (userSession == null) {
logger.error("User Session is not valid");
throw new nonValidSessionException("The session does not exists");
} else {
if (isSessionEnabled) {
//update the last access
int sessionTimeout =
new Integer(valveConf.getSessionConfig().getSessionTimeout()).intValue();
if (sessionTimeout >= 0) {
long lastAccessTime = getCurrentTime();
if (lastAccessTime > 0) {
logger.debug("New access time: " + lastAccessTime);
userSession.setSessionLastAccessTime(lastAccessTime);
sessions.addSession(userID, userSession);
}
}
}
}
}
return userSession;
}
/**
* Gets the current time
*
* @return current time
*/
public long getCurrentTime() {
long currentTime = System.currentTimeMillis();
return currentTime;
}
/**
* Deletes all cookies that start with "gsa"
*
* @param request HTTP request
* @param response HTTP response
*/
public void deleteCookies(HttpServletRequest request,
HttpServletResponse response) {
// Retrieve cookies
Cookie[] allCookies = request.getCookies();
try {
// Protection
if (allCookies != null) {
// Look for the authentication cookie
for (int i = 0; i < allCookies.length; i++) {
logger.debug("Cookie: " + allCookies[i].getName());
//look for all the cookies start with "gsa" and delete them
if ((allCookies[i].getName()).startsWith("gsa")) {
Cookie gsaCookie =
new Cookie(allCookies[i].getName(), allCookies[i].getValue());
gsaCookie.setMaxAge(0);
response.addCookie(gsaCookie);
// Debug
if (logger.isDebugEnabled())
logger.debug("GSA cookie: [" +
gsaCookie.getName() +
" has been deleted ]");
}
}
}
} catch (Exception e) {
logger.error("Error when deleting cookies: " + e.getMessage(), e);
}
}
/**
* Gets the redirect URL when it's needed to reauthenticate
*
* @param url request url
* @param loginUrl login url
*
* @return the redirect url
*/
public String redirectUrl(String url, String loginUrl) {
//redirect
String redirectUrl = null;
if (url != null) {
redirectUrl = loginUrl + "?returnPath=" + url;
} else {
redirectUrl = loginUrl;
}
logger.debug("redirecting to " + redirectUrl);
return redirectUrl;
}
/**
* Updates in the session the last access time
*
* @param userSession user session
*/
public void updateLastAccessTime(UserSession userSession) {
long currentTime = System.currentTimeMillis();
logger.debug("Last access time: " +
userSession.getSessionLastAccessTime());
userSession.setSessionLastAccessTime(currentTime);
}
/**
* Reads only once the configuration parameter needed
*
*/
private void setValveConf() {
logger.debug("Starting setValveConf method");
try {
if (sessionVarsSet == -1) {
//These vars have never been set for this instance
isSessionEnabled =
new Boolean(valveConf.getSessionConfig().isSessionEnabled()).booleanValue();
logger.debug("Setting isSessionEnabled: " + isSessionEnabled);
isKerberos =
new Boolean(valveConf.getKrbConfig().isKerberos()).booleanValue();
logger.debug("Setting isKerberos: " + isKerberos);
if (isSessionEnabled) {
sendCookies =
new Boolean(valveConf.getSessionConfig().getSendCookies()).booleanValue();
} else {
sendCookies = false;
}
logger.debug("Setting sendCookies: " + sendCookies);
isSAML =
new Boolean(valveConf.getSAMLConfig().isSAML()).booleanValue();
logger.debug("Setting isSAML: " + isSAML);
authCookieName = valveConf.getAuthCookieName();
logger.debug("Setting authCookieName: " + authCookieName);
internalURL = valveConf.getLoginUrl();
logger.debug("Setting internalURL: " + internalURL);
//Set the the following var to a value distinct than "-1"
sessionVarsSet = 0;
}
} catch (Exception e) {
logger.error("Error reading config parameters. Check config file");
}
}
}