diff --git a/pom.xml b/pom.xml
index c1d1e65..60ed09f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,11 +151,6 @@
htmlcleaner
2.21
-
- org.springframework
- spring-test
- 5.0.1.RELEASE
-
com.google.guava
guava
@@ -212,7 +207,6 @@
org.springframework
spring-test
5.0.7.RELEASE
- test
org.apache.commons
@@ -226,5 +220,14 @@
1.4
test
-
+
+ io.lettuce
+ lettuce-core
+ 5.0.3.RELEASE
+
+
+ com.github.kstyrc
+ embedded-redis
+ 0.6
+
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnController.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnController.java
index 8cfea54..9977f53 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnController.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnController.java
@@ -179,6 +179,7 @@ public AuthnResult authenticate(AuthnSession session, HttpServletRequest request
if (request != null) {
session.updateIncomingCookies(request);
}
+ setupSessionCookies(session, response);
AuthnResult result;
switch (maybeForceAuthnState(session)) {
@@ -639,9 +640,6 @@ private void renderUniversalLoginForm(AuthnSession session, HttpServletRequest r
logger.info(session.logMessage("Rendering Universal Login Form."));
gsaLogger.log(session.getRequestId(), "Rendering Universal Login Form.");
- if (null == SessionUtil.findGsaSessionId(request)) {
- addAuthnSessionCookie(session, response);
- }
updateOutgoingCookies(session, response);
PrintWriter writer = ServletBase.initNormalResponse(response);
writer.print(session.getUniversalLoginForm()
@@ -651,6 +649,11 @@ private void renderUniversalLoginForm(AuthnSession session, HttpServletRequest r
writer.close();
}
+ public void setupSessionCookies(AuthnSession session,
+ HttpServletResponse response) {
+ addAuthnSessionCookie(session, response);
+ }
+
private void addAuthnSessionCookie(AuthnSession session, HttpServletResponse response) {
SessionView view = session.getSnapshot().getView();
CookieStore outgoingCookies = GCookie.makeStore();
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSession.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSession.java
index 6056e53..bb38c68 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSession.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSession.java
@@ -42,6 +42,10 @@
import com.google.enterprise.secmgr.ulf.UniversalLoginFormHtml;
import com.google.enterprise.sessionmanager.SessionManagerInterfaceBase;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ArrayDeque;
+import java.util.Queue;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.NameID;
@@ -50,7 +54,6 @@
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
@@ -65,13 +68,83 @@
* An object to represent the session information for authentication.
*/
@ThreadSafe
-public class AuthnSession {
+public class AuthnSession implements Serializable {
+
+ // fields that are required for serializing/restoring SamlContext
+ private String samlRequest;
+ private String relayState;
+ private String serverName;
+ private int serverPort;
+ private String scheme;
+ private String requestURI;
+
+
+ public static final String AUTHN_SESSION = "AuthnSession";
+
private static final Logger logger = Logger.getLogger(AuthnSession.class.getName());
private static final LogClient GSA_LOGGER = new LogClient("Security Manager");
@Inject private static AuthnSessionManager sessionManager;
private static boolean secureSearchApiMode = false;
+ private boolean hasModifications = false;
+
+ public boolean hasModifications() {
+ return hasModifications;
+ }
+
+ public void resetModifications() {
+ this.hasModifications = false;
+ }
+
+ public String getSamlRequest() {
+ return samlRequest;
+ }
+
+ public void setSamlRequest(String samlRequest) {
+ this.samlRequest = samlRequest;
+ }
+
+ public String getRelayState() {
+ return relayState;
+ }
+
+ public void setRelayState(String relayState) {
+ this.relayState = relayState;
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public void setServerPort(int serverPort) {
+ this.serverPort = serverPort;
+ }
+
+ public void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+ public void setRequestURI(String requestURI) {
+ this.requestURI = requestURI;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ public int getServerPort() {
+ return serverPort;
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ public String getRequestURI() {
+ return requestURI;
+ }
+
/**
* The authentication controller is a state machine. This is the set of
* possible states for the controller.
@@ -94,7 +167,7 @@ public enum AuthnState {
*/
@Immutable
@ParametersAreNonnullByDefault
- public static final class ClientPair {
+ public static final class ClientPair implements Serializable {
@Nonnull private final CredentialsGatherer gatherer;
@Nonnull private final AuthnMechanism mechanism;
@@ -125,16 +198,17 @@ public AuthnMechanism getMechanism() {
@GuardedBy("itself")
private final CookieStore incomingCookies;
private final String sessionId;
- private final SecurityManagerConfig config;
+
+ private transient SecurityManagerConfig config;
@GuardedBy("this")
private AuthnState state;
@GuardedBy("this")
- private HttpServletRequest request;
+ private transient HttpServletRequest request;
@GuardedBy("this")
private String requestId;
@GuardedBy("this")
- private SAMLMessageContext samlSsoContext;
+ private transient SAMLMessageContext samlSsoContext;
@GuardedBy("this")
private URL authnEntryUrl;
@GuardedBy("this")
@@ -142,7 +216,7 @@ public AuthnMechanism getMechanism() {
@GuardedBy("this")
private UniversalLoginForm ulForm;
@GuardedBy("this")
- private Iterator clientPairs;
+ private Queue clientPairs;
@GuardedBy("this")
private CredentialsGathererElement clientElement;
@GuardedBy("this")
@@ -150,7 +224,7 @@ public AuthnMechanism getMechanism() {
@GuardedBy("this")
private AuthnSessionState sessionState;
- private AuthnSession(SecurityManagerConfig config, String sessionId) {
+ public AuthnSession(SecurityManagerConfig config, String sessionId) {
incomingCookies = GCookie.makeStore();
this.sessionId = sessionId;
this.requestId = null;
@@ -161,6 +235,23 @@ private AuthnSession(SecurityManagerConfig config, String sessionId) {
logger.info(logMessage("created new session."));
}
+ @VisibleForTesting
+ public static AuthnSession newInstance()
+ throws IOException {
+ return new AuthnSession(ConfigSingleton.getConfig(), SessionUtil.generateId());
+ }
+
+ @VisibleForTesting
+ public static AuthnSession newInstance(String sessionId)
+ throws IOException {
+ return new AuthnSession(ConfigSingleton.getConfig(), sessionId);
+ }
+
+ @VisibleForTesting
+ public static AuthnSession newInstance(SecurityManagerConfig config) {
+ return new AuthnSession(config, SessionUtil.generateId());
+ }
+
/**
* Sets secure search api mode.
* @param mode true for secure search api mode
@@ -177,159 +268,18 @@ public static synchronized boolean isSecureSearchApiMode() {
return secureSearchApiMode;
}
- /**
- *
- * Gets the authentication-session object for a session, creating it if needed.
- *
- * @param request An HTTP servlet request containing a GSA session ID cookie.
- * @param createGsaSmSessionIfNotExist Indicate whether to create a session
- * in GSA session manager when there is no GSA_SESSION_ID cookie passed
- * through via the request.
- * This is set to true only when called by SamlAuthn#doGet as it's the
- * only entry point of authn process. In later stage of authn process, we
- * return null if the user agent is not able to keep and pass the cookie.
- * @return The authentication-session object for the given session ID, or null
- * if we couldn't find the GSA session ID cookie.
- */
- public static AuthnSession getInstance(HttpServletRequest request,
- boolean createGsaSmSessionIfNotExist) throws IOException {
- String sessionId = SessionUtil.findGsaSessionId(request);
-
- // Parts of the security manager assume the GSA session ID and the
- // security manager session ID are equal, so if the user has turned off
- // cookies, we want to throw an exception here so our caller can tell
- // them to turn cookies on. However, unit tests don't set the
- // GSA_SESSION_ID cookies, so the error is conditional.
-
- // TODO: Once the sec-mgr supports offboard, the cookie
- // domain limit might prevent the cookie from reaching us. We might
- // have to transfer the GSA session id in some other way (e.g. the SAML
- // request). We'll need to make sure this code gets updated
- // appropriately.
- if (sessionId == null) {
- logger.warning("Unable to find GSA session ID.");
- if (secureSearchApiMode) {
- sessionId = SessionUtil.generateId();
- } else if (createGsaSmSessionIfNotExist) {
- SessionManagerInterfaceBase gsaSm = SessionUtil.getGsaSessionManager();
- sessionId = gsaSm.createSession();
- // The below key-value pair is required by EFE in
- // {@code enterprise.frontend.AuthN#getSessionExpiration}.
- //
- // Here we first set it to {@code AuthNConstants.NEVER_EXPIRES} and
- // later if the user starts a search through EFE with the same
- // GSA_SESSION_ID, EFE will query secmgr for user authn state and secmgr
- // will return expire time in the response and EFE will update the
- // session accordingly.
- gsaSm.setValue(sessionId, AuthNConstants.AUTHN_SESSION_EXPIRE_TIME_KEY,
- AuthNConstants.NEVER_EXPIRES);
- } else {
- return null;
- }
- }
-
- AuthnSession session = getInstanceInternal(sessionId);
- if (secureSearchApiMode) {
- session.setRequest(request);
- }
- String requestId = SessionUtil.findGsaRequestId(request);
-
- if (requestId != null) {
- session.setRequestId(requestId);
- logger.fine("RequestId found: " + requestId + " for session " + sessionId);
- } else {
- logger.fine("RequestId was not found for this request. session=" + sessionId);
- }
- return session;
- }
-
- /**
- * Gets the user's session if exists. If not create a new one.
- * @param userId The user string
- * @param credentialGroup The user's credential group
- * @param password The user's password
- * @return The user's session.
- * @throws IOException if failed to contact session manager.
- */
- public static AuthnSession getInstanceForUser(String userId, String credentialGroup,
- String password) throws IOException {
- AuthnSession session;
+ public void addCredentials(String userId, String credentialGroup, String password) {
String namespace = Strings.isNullOrEmpty(credentialGroup) ? CredentialGroup.DEFAULT_NAME
- : credentialGroup;
+ : credentialGroup;
String credPassword = (password == null) ? "" : password;
String[] parsed = IdentityUtil.parseNameAndDomain(userId);
- // TODO: Add support for multiple credential groups.
- SecurityManagerConfig config = ConfigSingleton.getConfig();
AuthnPrincipal user = AuthnPrincipal.make(parsed[0],
config.getCredentialGroup(namespace).getName(), parsed[1]);
- // In real, no end-user submit concurrent queries.
- // We don't support concurrent search request from one end-user for now.
- session = sessionManager.getUserSession(user);
- if (session == null) {
- session = createInstance();
- sessionManager.registerSession(session);
- sessionManager.addUserSession(user, session.getSessionId());
- session.addCredentials(
- config.getCredentialGroup(namespace).getAuthority(),
- user, CredPassword.make(credPassword));
- logger.info("Created session " + session.getSessionId() + " for user " + userId
- + " in " + namespace);
- } else {
- logger.info("Got existing session " + session.getSessionId() + " for user " + userId);
- }
-
- return session;
+ addCredentials(config.getCredentialGroup(namespace).getAuthority(), user,
+ CredPassword.make(credPassword));
}
- /**
- * Gets the authentication-session object for a session, creating it if needed.
- *
- * @param sessionId The ID string for the session.
- * @return The authentication-session object for the given session ID.
- */
- public static AuthnSession getInstance(String sessionId)
- throws IOException {
- Preconditions.checkArgument(SessionUtil.isValidId(sessionId));
- return getInstanceInternal(sessionId);
- }
-
- @VisibleForTesting
- public static AuthnSession newInstance()
- throws IOException {
- return getInstanceInternal(SessionUtil.generateId());
- }
-
- @VisibleForTesting
- public static AuthnSession createInstance()
- throws IOException {
- String sessionId = SessionUtil.getGsaSessionManager().createSession();
- logger.info("created session " + sessionId);
- return getInstanceInternal(sessionId);
- }
-
- @VisibleForTesting
- public static AuthnSession getInstance(SecurityManagerConfig config) {
- Preconditions.checkNotNull(config);
- return getInstanceInternal(SessionUtil.generateId(), config);
- }
-
- private static AuthnSession getInstanceInternal(String sessionId)
- throws IOException {
- return getInstanceInternal(sessionId, ConfigSingleton.getConfig());
- }
-
- private static AuthnSession getInstanceInternal(String sessionId, SecurityManagerConfig config) {
- AuthnSession session;
- synchronized (sessionManager) {
- session = sessionManager.getSession(sessionId);
- if (session == null) {
- session = new AuthnSession(config, sessionId);
- sessionManager.registerSession(session);
- }
- }
- return session;
- }
/**
* Gets a snapshot of the session state.
@@ -364,22 +314,22 @@ synchronized AuthnSessionState getSessionState() {
return sessionState;
}
- // For use by AuthnController.
void updateSessionState(final AuthnSessionState delta) {
if (delta.isEmpty()) {
return;
}
logger.info(logMessage("Modify session state:\n" + delta));
synchronized (this) {
+ hasModifications = true;
sessionState = sessionState.add(delta);
}
}
- // For use by CommandsServlet to overrite the current state
public void importSessionState(final AuthnSessionState delta) {
logger.info(logMessage("Import session state:\n" + delta));
synchronized (this) {
sessionState = AuthnSessionState.empty().add(delta);
+ hasModifications = true;
}
}
@@ -465,6 +415,7 @@ public void updateIncomingCookies(HttpServletRequest request) {
GSA_LOGGER.info(requestId, GCookie.requestCookiesMessage(
"Incoming cookies from user agent", requestCookies));
synchronized (incomingCookies) {
+ hasModifications = true;
incomingCookies.clear();
incomingCookies.addAll(requestCookies);
}
@@ -566,6 +517,10 @@ public synchronized SAMLMessageContext getSamlSs
return samlSsoContext;
}
+ public synchronized void setSamlSsoContext(SAMLMessageContext context) {
+ this.samlSsoContext = context;
+ }
+
/**
* Gets the number of times the user has been prompted by the ULF.
*
@@ -673,12 +628,14 @@ public synchronized void setStateInCredentialsGatherer(
Preconditions.checkNotNull(credentialsGatherers);
setState(AuthnState.IN_CREDENTIALS_GATHERER, AuthnState.AUTHENTICATING);
ImmutableList.Builder builder = ImmutableList.builder();
+ this.clientPairs = new ArrayDeque<>();
for (CredentialsGatherer client : credentialsGatherers) {
for (AuthnMechanism mechanism : config.getMechanisms()) {
- builder.add(new ClientPair(client, mechanism));
+ ClientPair clientPair = new ClientPair(client, mechanism);
+ builder.add(clientPair);
+ clientPairs.add(clientPair);
}
}
- clientPairs = builder.build().iterator();
}
private synchronized void clearCredentialsGathererState() {
@@ -740,10 +697,10 @@ synchronized CredentialsGathererElement getCredentialsGathererElement() {
*/
synchronized ClientPair getNextClientPair() {
assertState(AuthnState.IN_CREDENTIALS_GATHERER);
- if (!clientPairs.hasNext()) {
+ if (clientPairs.isEmpty()) {
return null;
}
- ClientPair clientPair = clientPairs.next();
+ ClientPair clientPair = clientPairs.remove();
maybeFailForTest();
return clientPair;
}
@@ -788,6 +745,7 @@ private void setStateInternal(AuthnState targetState) {
logger.info(logMessage("State transition from " + state + " to " + targetState));
}
state = targetState;
+ hasModifications = true;
}
private void illegalStateTransition(AuthnState targetState) {
@@ -900,4 +858,16 @@ private void maybeFailForTest() {
throw new RuntimeException("Forced test failure.");
}
}
+
+ public void setClientElement(
+ CredentialsGathererElement clientElement) {
+ this.clientElement = clientElement;
+ }
+
+
+ private void readObject(ObjectInputStream is)
+ throws ClassNotFoundException, IOException {
+ is.defaultReadObject();
+ this.config = ConfigSingleton.getConfig();
+ }
}
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManager.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManager.java
index da6c530..ad43e59 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManager.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManager.java
@@ -15,18 +15,19 @@
package com.google.enterprise.secmgr.authncontroller;
import com.google.enterprise.secmgr.identity.AuthnPrincipal;
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
/**
* The authentication-session manager. Tracks sessions and when they were last
* used. May eventually support garbage collection and session expiration.
*/
public interface AuthnSessionManager {
+
/**
- * Register an authentication session.
- *
- * @param session The session to register.
+ * Creates new session and attach it to request.
*/
- public void registerSession(AuthnSession session);
+ AuthnSession createAttachedSession(HttpServletRequest request) throws IOException;
/**
* Get a previously-registered session.
@@ -34,26 +35,15 @@ public interface AuthnSessionManager {
* @param sessionId The ID of the saved session.
* @return The saved session, or null if there's none.
*/
- public AuthnSession getSession(String sessionId);
+ AuthnSession findSessionById(String sessionId);
- /**
- * Associates a user to a session id.
- * @param user The user
- * @param sessionId The ID of the session.
- */
- public void addUserSession(AuthnPrincipal user, String sessionId);
+ AuthnSession findSession(HttpServletRequest request);
- /**
- * Gets the session for the user.
- * @param user The user
- * @return The session for the user. Returns null if there is no existing session.
- */
- public AuthnSession getUserSession(AuthnPrincipal user);
+ AuthnSession createSession() throws IOException;
- /**
- * Set the amount of time a session is allowed to be idle before it's expired.
- *
- * @param sessionIdleMillis The idle time in milliseconds.
- */
- public void setSessionIdleMillis(long sessionIdleMillis);
+ void saveSession(AuthnSession authnSession);
+
+ void updateSessionTTL(AuthnSession authnSession);
+
+ void setSessionIdleMillis(long sessionIdleMillis);
}
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManagerImpl.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManagerImpl.java
index c6d9d09..3a461f1 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManagerImpl.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionManagerImpl.java
@@ -15,16 +15,19 @@
package com.google.enterprise.secmgr.authncontroller;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import com.google.enterprise.secmgr.common.SecurityManagerUtil;
+import com.google.enterprise.secmgr.common.SessionUtil;
+import com.google.enterprise.secmgr.config.ConfigSingleton;
import com.google.enterprise.secmgr.identity.AuthnPrincipal;
+import com.google.enterprise.sessionmanager.RedisRepository;
import com.google.inject.Singleton;
-import org.joda.time.DateTimeUtils;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Formatter;
+import java.util.Random;
+import javax.servlet.http.HttpServletRequest;
-import java.util.List;
-import java.util.Map;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
@@ -39,85 +42,90 @@
public final class AuthnSessionManagerImpl implements AuthnSessionManager {
private static final Logger logger = Logger.getLogger(AuthnSessionManagerImpl.class.getName());
- // User to session id map.
- @GuardedBy("this") private final Map userSessionMap;
- @GuardedBy("this") private final Map sessionMap;
- @GuardedBy("this") private final Map refTimeMap;
@GuardedBy("this") private long sessionIdleMillis;
+ private static final Random random = new SecureRandom();
+
+ @Inject
+ private RedisRepository redisRepository;
+
@Inject
@VisibleForTesting
AuthnSessionManagerImpl() {
- sessionMap = Maps.newHashMap();
- userSessionMap = Maps.newHashMap();
- refTimeMap = Maps.newHashMap();
sessionIdleMillis = SecurityManagerUtil.getGsaSessionIdleMillis();
}
+ /* New Session Functions*/
+
@Override
- public synchronized void registerSession(AuthnSession session) {
- long now = DateTimeUtils.currentTimeMillis();
- deleteExpiredSessions(now);
- sessionMap.put(session.getSessionId(), session);
- refTimeMap.put(session, now);
- logger.info("Register session " + session.getSessionId());
+ public AuthnSession findSessionById(String sessionId) {
+ return redisRepository.loadSession(sessionId);
}
@Override
- public synchronized AuthnSession getSession(String sessionId) {
- long now = DateTimeUtils.currentTimeMillis();
- deleteExpiredSessions(now);
- AuthnSession session = sessionMap.get(sessionId);
+ public AuthnSession findSession(HttpServletRequest request) {
+ AuthnSession session = (AuthnSession) request.getAttribute(AuthnSession.AUTHN_SESSION);
if (session != null) {
- refTimeMap.put(session, now);
+ return session;
+ } else {
+ String sessionId = SessionUtil.findGsaSessionId(request);
+ if (sessionId == null) {
+ return null;
+ }
+ session = findSessionById(sessionId);
+ request.setAttribute(AuthnSession.AUTHN_SESSION, session);
+ return session;
}
+ }
+
+ @Override
+ public AuthnSession createAttachedSession(HttpServletRequest request) throws IOException {
+ AuthnSession session = createSession();
+ request.setAttribute(AuthnSession.AUTHN_SESSION, session);
return session;
}
@Override
- public synchronized void addUserSession(AuthnPrincipal user, String sessionId) {
- userSessionMap.put(user, sessionId);
+ public AuthnSession createSession() throws IOException {
+ AuthnSession session = new AuthnSession(ConfigSingleton.getConfig(), genSessionId());
+ return session;
}
@Override
- public synchronized AuthnSession getUserSession(AuthnPrincipal user) {
- String sessionId = userSessionMap.get(user);
- if (sessionId != null) {
- return getSession(sessionId);
- }
- return null;
+ public void saveSession(AuthnSession authnSession) {
+ redisRepository.storeSession(authnSession, sessionIdleMillis);
}
@Override
- public synchronized void setSessionIdleMillis(long sessionIdleMillis) {
- this.sessionIdleMillis = sessionIdleMillis;
+ public void updateSessionTTL(AuthnSession authnSession) {
+ redisRepository.updateSessionTTL(authnSession);
}
- @GuardedBy("this")
- private void deleteExpiredSessions(long now) {
- long minRefTime = (now - sessionIdleMillis);
- List toDelete = Lists.newArrayList();
- for (Map.Entry entry : refTimeMap.entrySet()) {
- if (entry.getValue() < minRefTime) {
- toDelete.add(entry.getKey());
- }
- }
- for (AuthnSession session : toDelete) {
- logger.fine("Deleting expired session " + session.getSessionId());
- sessionMap.remove(session.getSessionId());
- refTimeMap.remove(session);
- AuthnPrincipal user = getUser(session);
- if (user != null) {
- userSessionMap.remove(user);
- }
+ private String genSessionId() {
+ byte[] randomBytes = new byte[16];
+ random.nextBytes(randomBytes);
+ StringBuilder sidBuilder =
+ new StringBuilder(randomBytes.length * 2);
+ random.nextBytes(randomBytes);
+ sidBuilder.setLength(0);
+ Formatter f = new Formatter(sidBuilder);
+ for (byte b : randomBytes) {
+ f.format("%02x", b);
}
+ return f.toString();
}
+
@VisibleForTesting
long getSessionIdleMillis() {
return sessionIdleMillis;
}
+ @Override
+ public synchronized void setSessionIdleMillis(long sessionIdleMillis) {
+ this.sessionIdleMillis = sessionIdleMillis;
+ }
+
@VisibleForTesting
AuthnPrincipal getUser(AuthnSession session) {
SessionView view = session.getSnapshot().getPrimaryVerifiedView();
@@ -127,15 +135,8 @@ AuthnPrincipal getUser(AuthnSession session) {
return null;
}
- @VisibleForTesting
- Long getSessionRef(AuthnSession session) {
- return refTimeMap.get(session);
- }
-
@VisibleForTesting
public synchronized void reset() {
- sessionMap.clear();
- refTimeMap.clear();
sessionIdleMillis = SecurityManagerUtil.getGsaSessionIdleMillis();
}
}
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionState.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionState.java
index 33006c4..efd6bdc 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionState.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/AuthnSessionState.java
@@ -43,6 +43,7 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
+import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
@@ -60,7 +61,7 @@
*/
@Immutable
@ParametersAreNonnullByDefault
-public final class AuthnSessionState {
+public final class AuthnSessionState implements Serializable {
/**
* The possible instruction operations supported here.
@@ -553,7 +554,7 @@ Iterable getMechAuthorities(AuthnAuthority authority) {
*/
@Immutable
@ParametersAreNonnullByDefault
- public static final class Summary {
+ public static final class Summary implements Serializable {
@Nonnull private final ImmutableList credentialGroups;
@Nonnull private final ImmutableMap> cookiesMap;
@Nonnull private final ImmutableSetMultimap credentialsMap;
@@ -737,7 +738,7 @@ public AuthnSessionState build() {
*/
@Immutable
@ParametersAreNonnullByDefault
- public static final class Instruction {
+ public static final class Instruction implements Serializable {
@Nonnull private final Operation operation;
@Nonnull private final AuthnAuthority authority;
@Nonnull private final Object operand;
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGatherer.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGatherer.java
index 98127b8..4e78f11 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGatherer.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGatherer.java
@@ -16,6 +16,7 @@
import java.io.IOException;
+import java.io.Serializable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -25,7 +26,7 @@
* authentication controller to manage the credentials-gathering process.
*/
@ParametersAreNonnullByDefault
-public interface CredentialsGatherer {
+public interface CredentialsGatherer extends Serializable {
/**
* Will this credentials gatherer handle a given session view?
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGathererElement.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGathererElement.java
index 6667283..a1330ad 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGathererElement.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/CredentialsGathererElement.java
@@ -14,6 +14,7 @@
package com.google.enterprise.secmgr.authncontroller;
+import java.io.Serializable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
@@ -23,7 +24,7 @@
* process a single identity.
*/
@ParametersAreNonnullByDefault
-public interface CredentialsGathererElement {
+public interface CredentialsGathererElement extends Serializable {
/**
* Gets the credentials gatherer this is an element for.
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionSnapshot.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionSnapshot.java
index 65f4977..cc6c6df 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionSnapshot.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionSnapshot.java
@@ -22,10 +22,14 @@
import com.google.enterprise.secmgr.common.GCookie;
import com.google.enterprise.secmgr.common.SessionUtil;
import com.google.enterprise.secmgr.config.AuthnMechanism;
+import com.google.enterprise.secmgr.config.ConfigSingleton;
import com.google.enterprise.secmgr.config.CredentialGroup;
import com.google.enterprise.secmgr.config.SecurityManagerConfig;
import com.google.enterprise.secmgr.identity.Verification;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
import org.joda.time.DateTimeUtils;
import org.joda.time.format.ISODateTimeFormat;
@@ -45,12 +49,12 @@
*/
@Immutable
@ParametersAreNonnullByDefault
-public final class SessionSnapshot {
+public final class SessionSnapshot implements Serializable {
@Nonnull private final String sessionId;
@Nullable private final String requestId;
//TODO: Replace this with an immutable info.
@Nullable private final HttpServletRequest request;
- @Nonnull private final SecurityManagerConfig config;
+ @Nonnull transient private SecurityManagerConfig config;
@Nullable private final URL authnEntryUrl;
@Nonnull private final ImmutableCollection userAgentCookies;
@Nonnull private final AuthnSessionState state;
@@ -264,4 +268,10 @@ public String toString() {
builder.append("}");
return builder.toString();
}
+
+ private void readObject(ObjectInputStream is)
+ throws ClassNotFoundException, IOException {
+ is.defaultReadObject();
+ this.config = ConfigSingleton.getConfig();
+ }
}
diff --git a/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionView.java b/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionView.java
index 6a8b7ff..3de18b4 100644
--- a/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionView.java
+++ b/src/main/java/com/google/enterprise/secmgr/authncontroller/SessionView.java
@@ -33,6 +33,7 @@
import com.google.enterprise.secmgr.identity.Verification;
import com.google.enterprise.secmgr.identity.VerificationStatus;
+import java.io.Serializable;
import java.net.URL;
import javax.annotation.Nonnegative;
@@ -48,7 +49,7 @@
*/
@Immutable
@ParametersAreNonnullByDefault
-public abstract class SessionView {
+public abstract class SessionView implements Serializable {
@Nonnull protected final SessionSnapshot snapshot;
@GuardedBy("this") @Nullable protected AuthnSessionState.Summary summary;
diff --git a/src/main/java/com/google/enterprise/secmgr/authzcontroller/Authorizer.java b/src/main/java/com/google/enterprise/secmgr/authzcontroller/Authorizer.java
index 735717c..2a4bbd1 100644
--- a/src/main/java/com/google/enterprise/secmgr/authzcontroller/Authorizer.java
+++ b/src/main/java/com/google/enterprise/secmgr/authzcontroller/Authorizer.java
@@ -15,6 +15,7 @@
*/
package com.google.enterprise.secmgr.authzcontroller;
+import com.google.enterprise.secmgr.authncontroller.AuthnSession;
import com.google.enterprise.secmgr.common.Resource;
import com.google.enterprise.secmgr.modules.AuthzResult;
@@ -32,11 +33,11 @@ public interface Authorizer {
* Runs this authorizer on some resources.
*
* @param resources Some resources to be authorized.
- * @param sessionId A sessionId to use for authorization.
+ * @param sessionId A session to use for authorization.
* @param enableFastAuthz If true, only use "fast" authorization mechanisms.
* @return The authorization results for the given resources.
*/
@Nonnull
- public AuthzResult apply(Collection resources, String sessionId,
+ AuthzResult apply(Collection resources, String sessionId,
boolean enableFastAuthz);
}
diff --git a/src/main/java/com/google/enterprise/secmgr/authzcontroller/AuthorizerImpl.java b/src/main/java/com/google/enterprise/secmgr/authzcontroller/AuthorizerImpl.java
index 005f27f..4bc8e52 100644
--- a/src/main/java/com/google/enterprise/secmgr/authzcontroller/AuthorizerImpl.java
+++ b/src/main/java/com/google/enterprise/secmgr/authzcontroller/AuthorizerImpl.java
@@ -21,7 +21,6 @@
import com.google.enterprise.secmgr.authncontroller.AuthnSessionManager;
import com.google.enterprise.secmgr.authncontroller.SessionSnapshot;
import com.google.enterprise.secmgr.common.Resource;
-import com.google.enterprise.secmgr.common.SessionUtil;
import com.google.enterprise.secmgr.modules.AuthzResult;
import com.google.inject.Singleton;
@@ -63,9 +62,8 @@ public static Authorizer getTestingInstance(AuthorizationController controller,
@Override
public AuthzResult apply(Collection resources, String sessionId,
boolean enableFastAuthz) {
- AuthnSession session = sessionManager.getSession(sessionId);
+ AuthnSession session = sessionManager.findSessionById(sessionId);
if (session == null) {
- logger.warning(SessionUtil.logMessage(sessionId, "Unable to find session"));
return AuthzResult.makeIndeterminate(Resource.resourcesToUrls(resources));
}
SessionSnapshot snapshot = session.getSnapshot();
diff --git a/src/main/java/com/google/enterprise/secmgr/common/Chain.java b/src/main/java/com/google/enterprise/secmgr/common/Chain.java
index b25a50f..c657e61 100644
--- a/src/main/java/com/google/enterprise/secmgr/common/Chain.java
+++ b/src/main/java/com/google/enterprise/secmgr/common/Chain.java
@@ -20,6 +20,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -42,7 +43,7 @@
*/
@Immutable
@ParametersAreNonnullByDefault
-public abstract class Chain implements Iterable {
+public abstract class Chain implements Iterable, Serializable {
@Nonnull private static final Chain> EMPTY_CHAIN = new EmptyChain