UIMA-6085 DUCC Web Server (WS) login session should be coordinated amongst DUCC head nodes

git-svn-id: https://svn.apache.org/repos/asf/uima/uima-ducc/trunk@1864509 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccCookies.java b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccCookies.java
index 5f2cfd1..64cbfa4 100644
--- a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccCookies.java
+++ b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccCookies.java
@@ -71,7 +71,8 @@
 	private static final String filter_users_style = "filter_users_style";
 	private static final String role = "role";
 	
-	private static final String uid = "uid";
+	private static final String key_uid = "uid";
+	private static final String key_loginToken = "loginToken";
 	
 	public static final String cookieStyleTable = duccCookiePrefix+table_style;
 	public static final String cookieStyleDate = duccCookiePrefix+date_style;
@@ -80,7 +81,8 @@
 	public static final String cookieStyleFilterUsers = duccCookiePrefix+filter_users_style;
 	public static final String cookieRole = duccCookiePrefix+role;
 	
-	public static final String cookieUid = duccCookiePrefix+uid;
+	public static final String cookieUid = duccCookiePrefix+key_uid;
+	public static final String cookieLoginToken = duccCookiePrefix+key_loginToken;
 	
 	public static final String valueStyleDateLong = "long";
 	public static final String valueStyleDateMedium = "medium";
@@ -103,6 +105,12 @@
 	public static final String valueRoleAdministrator = "administrator";
 	public static final String valueRoleUser = "user";
 
+	public static final int seconds_per_minute = 60;
+	public static final int seconds_per_hour = 60*seconds_per_minute;
+	public static final int seconds_per_day = 24*seconds_per_hour;
+	public static final int seconds_per_year = 365*seconds_per_day;
+	public static final int seconds_per_century = 100*seconds_per_year;
+	
 	protected static final String getCookieKey(String name) {
 		return duccCookiePrefix+"name";
 	}
@@ -137,18 +145,19 @@
 		return getCookie(null,request,name);
 	}
 	
-	protected static void putCookie(HttpServletResponse response, String name, String value) {
+	protected static void putCookie(HttpServletResponse response, String name, String value, int expiry) {
 		String methodName = "putCookie";
 		Cookie cookie = new Cookie(name, value);
 		cookie.setPath(cookieUri);
+		cookie.setMaxAge(expiry);
 		response.addCookie(cookie);
 		duccLogger.trace(methodName, null, messages.fetchLabel("name")+name+" "+messages.fetchLabel("value")+value);
 	}
 	
-	protected static void expireCookie(HttpServletResponse response, String name, String value) {
-		String methodName = "expireCookie";
+	protected static void putCookie(HttpServletResponse response, String name, String value) {
+		String methodName = "putCookie";
 		Cookie cookie = new Cookie(name, value);
-		cookie.setMaxAge(0);
+		cookie.setPath(cookieUri);
 		response.addCookie(cookie);
 		duccLogger.trace(methodName, null, messages.fetchLabel("name")+name+" "+messages.fetchLabel("value")+value);
 	}
@@ -272,16 +281,58 @@
 		return role;
 	}
 	
-	public static String getUid(HttpServletRequest request) {
-		String location = "getUid";
-		String uid = null;
+	public static String getLoginUid(HttpServletRequest request) {
+		String location = "getLoginUid";
+		String loginUid = null;
 		try {
 			String cookie = getCookie(null,request,cookieUid);
-			uid = cookie;
-			duccLogger.debug(location, jobid, cookieUid+":"+uid);
+			loginUid = cookie;
+			duccLogger.debug(location, jobid, cookieUid+":"+loginUid);
 		}
 		catch(Exception e) {
 		}
-		return uid;
+		return loginUid;
 	}
+	
+	public static void setLoginUid(HttpServletResponse response, String value) {
+		String location = "setLoginUid";
+		try {
+			putCookie(response, cookieUid, value);
+			duccLogger.debug(location, jobid, cookieUid+":"+value);
+		}
+		catch(Exception e) {
+		}
+	}
+	
+	public static String getLoginToken(HttpServletRequest request) {
+		String location = "getLoginToken";
+		String loginToken = null;
+		try {
+			String cookie = getCookie(null,request,cookieLoginToken);
+			loginToken = cookie;
+			duccLogger.debug(location, jobid, cookieLoginToken+":"+loginToken);
+		}
+		catch(Exception e) {
+		}
+		return loginToken;
+	}
+	
+	public static void setLoginToken(HttpServletResponse response, String value) {
+		setLoginToken(response, value, seconds_per_century);
+	}
+	
+	public static void expireLoginToken(HttpServletResponse response) {
+		setLoginToken(response, "expired", 0);
+	}
+	
+	private static void setLoginToken(HttpServletResponse response, String value, int expiry) {
+		String location = "setLoginToken";
+		try {
+			putCookie(response, cookieLoginToken, value, expiry);
+			duccLogger.debug(location, jobid, cookieLoginToken+":"+value);
+		}
+		catch(Exception e) {
+		}
+	}
+	
 }
diff --git a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandler.java b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandler.java
index 694b053..dbe8030 100644
--- a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandler.java
+++ b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandler.java
@@ -366,7 +366,7 @@
 		String methodName = "handleDuccServletAuthenticatorNotes";
 		duccLogger.trace(methodName, null, messages.fetch("enter"));
 		StringBuffer sb = new StringBuffer();
-		String uid = DuccCookies.getUid(request);
+		String uid = DuccCookies.getLoginUid(request);
 		String notes = duccAuthenticator.getNotes(uid);
 		if(notes != null) {
 			sb.append(notes);
diff --git a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandlerUserAuthentication.java b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandlerUserAuthentication.java
index b86babd..8fcf12b 100644
--- a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandlerUserAuthentication.java
+++ b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccHandlerUserAuthentication.java
@@ -95,7 +95,7 @@
 		StringBuffer sb = new StringBuffer();
 		try {
 			userId = duccWebSessionManager.getUserId(request);
-			boolean result = duccWebSessionManager.logout(request);
+			boolean result = duccWebSessionManager.logout(request, response, userId);
 			if(result) {
 				duccLogger.info(methodName, jobid, messages.fetch("logout ")+userId+" "+messages.fetch("success"));
 				sb.append("success");
@@ -168,7 +168,7 @@
 				if(ducc_runmode_pw.length() > 0) {
 					if(password != null) {
 						if(password.equals(ducc_runmode_pw)) {
-							duccWebSessionManager.login(request, userId);
+							duccWebSessionManager.login(request,response,userId);
 							sb.append("success");
 						}
 					}
@@ -201,7 +201,7 @@
 					IAuthenticationResult result2 = duccAuthenticator.isGroupMember(userId, domain, role);
 					duccLogger.debug(methodName, jobid, messages.fetch("login ")+userId+" "+"group reason: "+result2.getReason());
 					if(result1.isSuccess() && result2.isSuccess()) {
-						duccWebSessionManager.login(request, userId);
+						duccWebSessionManager.login(request,response,userId);
 						duccLogger.info(methodName, jobid, messages.fetch("login ")+userId+" "+messages.fetch("success"));
 						sb.append("success");
 					}
diff --git a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccWebSessionManager.java b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccWebSessionManager.java
index df58c50..0fa6d64 100644
--- a/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccWebSessionManager.java
+++ b/uima-ducc-web/src/main/java/org/apache/uima/ducc/ws/server/DuccWebSessionManager.java
@@ -19,13 +19,17 @@
 package org.apache.uima.ducc.ws.server;
 
 import java.security.SecureRandom;
-import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletResponse;
 
 import org.apache.uima.ducc.common.utils.DuccLogger;
 import org.apache.uima.ducc.common.utils.id.DuccId;
+import org.apache.uima.ducc.database.login.DbUserLogin;
+
+/*
+ * Class to manage user login/logout, in coordination with browser and cookies
+ */
 
 public class DuccWebSessionManager {
 	
@@ -38,20 +42,14 @@
 		return instance;
 	}
 	
+	private static DbUserLogin dbUserLogin = new DbUserLogin(duccLogger);
+	
 	private static SecureRandom sr = new SecureRandom();
 	
-	public static final String ducc_user_id = "ducc.user.id";
-	public static final String ducc_validation_id = "ducc.validation.id";
-	
 	private static final int SEGMENTS = 8;
-	
-	private class IdSet {
-		public String sessionId;
-		public String validationId;
-	}
-	
-	private ConcurrentHashMap<String,IdSet> map = new ConcurrentHashMap<String,IdSet>();
 
+	// Token given to browser.  When present upon subsequent calls
+	// user is considered logged-in.
 	private String generateValidationId() {
 		StringBuffer sb = new StringBuffer();
 		sb.append(sr.nextLong());
@@ -62,167 +60,98 @@
 		return sb.toString();
 	}
 	
-	private void iRemove(String method, String userId, IdSet idSet) {
-		String location = "iRemove"+"."+method;
-		if(idSet != null) {
-			duccLogger.info(location, jobid, "uid:"+userId);
-			duccLogger.info(location, jobid, "sid:"+idSet.sessionId);
-			duccLogger.info(location, jobid, "vid:"+idSet.validationId);
-		}
-	}
-	
-	private void iPut(String method, String userId, IdSet idSet) {
-		String location = "iPut"+"."+method;
-		if(idSet != null) {
-			duccLogger.info(location, jobid, "uid:"+userId);
-			duccLogger.info(location, jobid, "sid:"+idSet.sessionId);
-			duccLogger.info(location, jobid, "vid:"+idSet.validationId);
-		}
-	}
-	
-	public void login(HttpServletRequest request, String userId) {
+	// login user
+	public void login(HttpServletRequest request, HttpServletResponse response, String userId) {
 		String location = "login";
 		if(request == null) {
 			duccLogger.debug(location, jobid, "request is null");
 			return;
 		}
+		if(response == null) {
+			duccLogger.debug(location, jobid, "response is null");
+			return;
+		}
 		if(userId == null) {
 			duccLogger.debug(location, jobid, "userId is null");
 			return;
 		}
-		HttpSession session = request.getSession();
-		if(session == null) {
-			duccLogger.debug(location, jobid, "session is null");
-			return;
-		}
-		String sessionId =  session.getId();
-		if(sessionId == null) {
-			duccLogger.debug(location, jobid, "sessionId is null");
-			return;
-		}
-		iRemove(location, userId, map.get(userId));
+		// generate validation id
 		String validationId = generateValidationId();
-		session.setAttribute(ducc_validation_id, validationId);
-		session.setAttribute(ducc_user_id, userId);
-		IdSet idSet = new IdSet();
-		idSet.validationId = validationId;
-		idSet.sessionId = sessionId;
-		map.put(userId, idSet);
-		iPut(location, userId, map.get(userId));
+		// tell browser
+		DuccCookies.setLoginUid(response, userId);
+		DuccCookies.setLoginToken(response, validationId);
+		duccLogger.debug(location, jobid, userId, validationId);
+		// tell database
+		dbUserLogin.addOrReplace(userId, validationId);
 		return;
 	}
 	
-	public boolean logout(HttpServletRequest request) {
+	// louout user
+	public boolean logout(HttpServletRequest request, HttpServletResponse response, String userId) {
 		String location = "logout";
 		boolean retVal = false;
 		if(request == null) {
 			duccLogger.debug(location, jobid, "request is null");
 			return retVal;
 		}
-		HttpSession session = request.getSession();
-		if(session == null) {
-			duccLogger.debug(location, jobid, "session is null");
+		if(response == null) {
+			duccLogger.debug(location, jobid, "response is null");
 			return retVal;
 		}
-		String userId = (String) session.getAttribute(ducc_user_id);
 		if(userId == null) {
 			duccLogger.debug(location, jobid, "userId is null");
 			return retVal;
 		}
-		String validationId = (String) session.getAttribute(ducc_validation_id);
-		if(validationId == null) {
-			duccLogger.debug(location, jobid, "validationId is null");
-			return retVal;
-		}
-		String sessionId =  session.getId();
-		if(sessionId == null) {
-			duccLogger.debug(location, jobid, "sessionId is null");
-			return retVal;
-		}
-		IdSet idSet = map.get(userId);
-		if(idSet == null) {
-			duccLogger.debug(location, jobid, "idSet is null");
-			return retVal;
-		}
-		if(!validationId.equals(idSet.validationId)) {
-			duccLogger.debug(location, jobid, "given:"+validationId);
-			duccLogger.debug(location, jobid, "known:"+idSet.validationId);
-			duccLogger.debug(location, jobid, "validation mismatch!");
-			return retVal;
-		}
-		if(!sessionId.equals(idSet.sessionId)) {
-			duccLogger.debug(location, jobid, "given:"+sessionId);
-			duccLogger.debug(location, jobid, "known:"+idSet.sessionId);
-			duccLogger.debug(location, jobid, "session mismatch!");
-			return retVal;
-		}
-		session.removeAttribute(ducc_validation_id);
-		session.removeAttribute(ducc_user_id);
-		map.remove(userId);
-		iRemove(location, userId, idSet);
-		retVal = true;
+		retVal = isAuthentic(request);
+		// tell browser
+		DuccCookies.expireLoginToken(response);
+		// tell database
+		dbUserLogin.delete(userId);
 		return retVal;
 	}
 	
-	public boolean isAuthentic(HttpServletRequest request) {
-		String location = "isAuthentic";
-		if(request == null) {
-			duccLogger.debug(location, jobid, "request is null");
-			return false;
+	// check token from db with token present by browser cookie
+	private boolean stringCompare(String s1, String s2) {
+		boolean retVal = false;
+		if(s1 != null) {
+			if(s2 != null) {
+				retVal = s1.equals(s2);
+			}
 		}
-		HttpSession session = request.getSession();
-		if(session == null) {
-			duccLogger.debug(location, jobid, "session is null");
-			return false;
-		}
-		String userId = (String) session.getAttribute(ducc_user_id);
-		if(userId == null) {
-			duccLogger.debug(location, jobid, "userId is null");
-			return false;
-		}
-		String validationId = (String) session.getAttribute(ducc_validation_id);
-		if(validationId == null) {
-			duccLogger.debug(location, jobid, "validationId is null");
-			return false;
-		}
-		String sessionId =  session.getId();
-		if(sessionId == null) {
-			duccLogger.debug(location, jobid, "sessionId is null");
-			return false;
-		}
-		IdSet idSet = map.get(userId);
-		if(idSet == null) {
-			duccLogger.debug(location, jobid, "idSet is null");
-			return false;
-		}
-		if(!validationId.equals(idSet.validationId)) {
-			duccLogger.debug(location, jobid, "given:"+validationId);
-			duccLogger.debug(location, jobid, "known:"+idSet.validationId);
-			duccLogger.debug(location, jobid, "validation mismatch!");
-			return false;
-		}
-		if(!sessionId.equals(idSet.sessionId)) {
-			duccLogger.debug(location, jobid, "given:"+sessionId);
-			duccLogger.debug(location, jobid, "known:"+idSet.sessionId);
-			duccLogger.debug(location, jobid, "session mismatch!");
-			return false;
-		}
-		return true;
+		return retVal;
 	}
 	
+	// check if browser userid+token match same in db
+	public boolean isAuthentic(HttpServletRequest request) {
+		String location = "isAuthentic";
+		boolean retVal = false;
+		if(request == null) {
+			duccLogger.debug(location, jobid, "request is null");
+			return false;
+		}
+		String userId = getUserId(request);
+		// fetch browser
+		String s1 = DuccCookies.getLoginToken(request);
+		duccLogger.debug(location, jobid, "cookie  ", retVal, userId, s1);
+		// fetch database
+		String s2 = dbUserLogin.fetch(userId);
+		duccLogger.debug(location, jobid, "database", retVal, userId, s2);
+		// compare
+		retVal = stringCompare(s1,s2);
+		return retVal;
+	}
+	
+	// fetch userid from browser
 	public String getUserId(HttpServletRequest request) {
-		String retVal = null;
 		String location = "getUserId";
+		String retVal = null;
 		if(request == null) {
 			duccLogger.debug(location, jobid, "request is null");
 			return retVal;
 		}
-		HttpSession session = request.getSession();
-		if(session == null) {
-			duccLogger.debug(location, jobid, "session is null");
-			return retVal;
-		}
-		retVal = (String) session.getAttribute(ducc_user_id);
+		// fetch browser
+		retVal = DuccCookies.getLoginUid(request);
+		duccLogger.debug(location, jobid, retVal);
 		return retVal;
 	}
 }