SessionService.java revision 1ac84aa9c86f7f73015bc5fea5bf17e776cf78da
/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at opensso/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id: SessionService.java,v 1.37 2010/02/03 03:52:54 bina Exp $
*
* Portions Copyrighted 2010-2014 ForgeRock AS.
*/
/**
* This class represents a Session Service
*/
public class SessionService {
/**
* Session Service Thread Pool for Session
* Handler Tasks.
*/
/**
* Our Session Service Singleton Service Implementation Instance.
*/
/**
* AM Session Repository for Session Persistence.
*/
/**
* SSO Token Manager Instance Reference.
*/
/**
* Globals
*/
public static int maxSessions = 10000;
private static int numberOfActiveSessions = 0;
private static SessionMaxStats maxSessionStats;
private static boolean logStatus = false;
private static final String httpSessionTrackingCookieName =
"JSESSIONID");
private static boolean cookieEncoding =
.equalsIgnoreCase("true"));
private static final String sunAppServerLBRoutingCookieName =
"com.iplanet.am.session.failover.sunAppServerLBRoutingCookieName",
"JROUTE");
private static final String httpSessionPropertyName =
"DSAMEInternalSession";
private static final String httpSessionOwnerListPropertyName =
"DSAMEInternalSession.ownerList";
private static final int DEFAULT_POOL_SIZE = 10;
// Session Constraints specific properties
private static final String SESSION_CONSTRAINT =
"iplanet-am-session-enable-session-constraint";
private static final String DENY_LOGIN_IF_DB_IS_DOWN =
"iplanet-am-session-deny-login-if-db-is-down";
private static final String MAX_WAIT_TIME_FOR_CONSTARINT =
"iplanet-am-session-constraint-max-wait-time";
private static final String CONSTRAINT_HANDLER =
"iplanet-am-session-constraint-handler";
private static final String SESSION_REPOSITORY_TYPE =
"iplanet-am-session-sfo-store-type";
// constants for permissions
private static boolean isPropertyNotificationEnabled = false;
protected static Set notificationProperties;
/*
* This token is used to satisfy the admin interfaces
*/
protected static boolean returnAppSession = Boolean
.valueOf(
"false")).booleanValue();
private static final Hashtable<String, InternalSession> sessionHandleTable = new Hashtable<String, InternalSession>();
private static String sessionServer;
private static String sessionServerPort;
private static String sessionServerProtocol;
private static String sessionServerURI;
private static String sessionServerID;
private static Set secondaryServerIDs;
// used for session trimming
private static boolean isSessionTrimmingEnabled = false;
/* the following group of members are for session constraints */
private static boolean isSessionConstraintEnabled = false;
private static boolean denyLoginIfDBIsDown = false;
private static String constraintHandler =
private static String thisSessionServer;
private static String thisSessionServerPortAsString;
private static int thisSessionServerPort;
private static String thisSessionURI;
private static String thisSessionServerProtocol;
private static String thisSessionServerID;
private static String thisSessionServerURL;
private static URL thisSessionServiceURL;
// Must be True to permit Session Failover HA to be available.
"true")).booleanValue();
// Must be True to permit Session Failover HA to be available.
"true")).booleanValue();
// Must be True to permit Session Failover HA to be available,
// but we default this to Disabled or Off for Now.
"false")).booleanValue();
/**
* Indicates whether to use crosstalk or session persistence to resolve remote sessions. Always true when session
* persistence/SFO is disabled.
*/
private static volatile boolean isReducedCrosstalkEnabled = true;
/**
* The number of minutes to retain {@link Session} objects in DESTROYED state while waiting
* for delete replication to occur if reduced cross-talk is enabled.
*/
private static volatile long reducedCrosstalkPurgeDelay = 5;
/**
*/
// Must be True to permit Session Failover HA to be available.
private static boolean isSiteEnabled = false; // If this is set to True and no Site is found, issues will arise
// Trying to resolve the serverID and will hang install and subsequent login attempts.
/**
* The following InternalSession is for the Authentication Service to use
* Profile API to fetch user profile.
*/
/**
* The URL Vector for ALL session events : SESSION_CREATION, IDLE_TIMEOUT,
* MAX_TIMEOUT, LOGOUT, REACTIVATION, DESTROY.
*/
private TokenIdFactory tokenIdFactory;
private CoreTokenConfig coreTokenConfig;
private SessionAdapter tokenAdapter;
private SessionInfoFactory sessionInfoFactory;
/**
* Static initialisation section will be called the first time the SessionService is initailised.
*
* Note: This function depends on the singleton pattern that the SessionService follows.
*/
private static void initialiseStatic() {
int poolSize = DEFAULT_POOL_SIZE;
int threshold = DEFAULT_THRESHOLD;
// Notification Thread Pool Size
try {
} catch (NumberFormatException e) {
"SessionService.<init>: incorrect thread pool size" + size +
"defaulting to " + DEFAULT_POOL_SIZE);
}
}
// Notification Thread Pool Threshold
try {
} catch (Exception e) {
"SessionService.<init>: incorrect thread threshold" + thres
+ "defaulting to " + DEFAULT_THRESHOLD);
}
}
// Establish Shutdown Manager.
new ShutdownListener() {
public void shutdown() {
}
}
);
if (threadPool != null) {
try {
maxSessions = 10000;
}
}
status = "INACTIVE";
}
}
/**
* Returns Session Service. If a Session Service already exists then it
* returns the existing one. Else it creates a new one and returns.
*/
public static SessionService getSessionService() {
if (sessionService == null) {
if (SystemProperties.isServerMode()) {
synchronized (SessionService.class) {
if (sessionService == null) {
sessionService = new SessionService();
}
} // End of synchronized Block.
}
}
return sessionService;
}
/**
* session tracking (currently hardcoded to "JSESSIONID")
*/
public static String getHttpSessionTrackingCookieName() {
return httpSessionTrackingCookieName;
}
/**
* Returns the Internal Session used by the Auth Services.
*
* @param domain Authentication Domain
* @param httpSession HttpSession
*/
try {
if (authSession == null) {
// Create a special InternalSession for Authentication Service
// ....
}
} catch (Exception e) {
return null;
}
}
/**
* Returns the restricted token
*
* @param masterSid master session id
* @param restriction TokenRestriction Object
* @return restricted token id
* @throws SessionException
*/
// we need to accommodate session failover situation
// first try
if (!isLocalServer(hostServerID)) {
}
if (!isLocalServer(hostServerID)) {
// TODO consider one retry attempt
throw new SessionException(SessionBundle
.getString("invalidSessionID")
+ masterSid);
} else {
return token;
}
}
}
}
}
/**
* This method is expected to only be called for local sessions
*/
// locate master session
throw new SessionException(SessionBundle
.getString("invalidSessionID")
+ masterSid);
}
}
// attempt to reuse the token if restriction is the same
if (restrictedSid == null) {
restrictedSid = new SessionID(SessionID.makeRelatedSessionID(generateEncryptedID(), session.getID()));
if (previousValue == null) {
} else {
}
}
return restrictedSid.toString();
}
/**
* Returns the Internal Session which can be used by services
*
* @param domain Authentication Domain
* @param httpSession HttpSession
*/
try {
// Create a special InternalSession which can be used as
// service token
// note that this session does not need failover protection
// as its scope is only this same instance
// more over creating an HTTP session by making a self-request
// results in dead-lock if called from within synchronized
// section in getSessionService()
.doPrivileged(new AdminDNAction());
return session;
} catch (Exception e) {
return null;
}
}
/**
* Creates a new Internal Session
*
* @param domain Authentication Domain
* @param httpSession Http Session
*/
try {
} catch (SessionException e) {
return null;
}
}
/**
* Creates a new Internal Session
*
* @param domain Authentication Domain
* @param httpSession Http Session
* @param forceHttpSessionCreation in session failover mode if this parameter is true and
* httpSession is null, it will cause SessionService to create a
* new Http session for failover purposes
*/
throws SessionException {
return createSession(domain);
}
// generate primary id
// generate session handle which looks like normal session id
// except it is not a valid session id
// and can not be used for anything other than destroySession
// TODO consider unifying RestrictedTokens and session handle
if (SystemProperties.isServerMode()) {
if (MonitoringUtil.isRunning()) {
}
}
return session;
}
/**
* Generates new encrypted ID string to be used as part of session id
*
* @return emcrypted ID string
*/
private String generateEncryptedID() {
// TODO note that this encryptedID string is kept only
// to make this compatible with older Java SDK clients
// which knew too much about the structure of the session id
// newer clients will mostly treat session id as opaque
//
}
/**
* Generates new SessionID
*
* @param domain session domain
* @param httpSession http session for failover purposes
* @return newly generated session id
* @throws SessionException
*/
throws SessionException {
do {
// AME-129 Required for Automatic Session Failover Persistence
}
// AME-129, always set a Storage Key regardless of persisting or not.
domain);
return sid;
}
/**
* extract http session id useable as http session cookie
*
* @param httpSession http session
* @return http session id
*/
return httpSession.getId();
}
/**
* Removes the Internal Session from the Internal Session table.
*
* @param sid Session ID
*/
boolean isSessionStored = false;
return null;
// Session Constraint
}
}
if (isSessionFailoverEnabled && isSessionStored) {
if (getUseInternalRequestRouting()) {
try {
} catch (Exception e) {
"SessionService : failed deleting session ", e);
}
} else {
}
}
return session;
}
if (isSessionFailoverEnabled) {
try {
} catch (Exception e) {
e);
}
}
}
return;
}
}
}
return;
// remove from sessionHandleTable (if present)
if (sessionHandle != null) {
}
}
/**
* Returns true if session failover is enabled
*/
public boolean isSessionFailoverEnabled() {
return isSessionFailoverEnabled;
}
/**
* Returns true if crosstalk is reduced (and if session failover is enabled).
*/
public boolean isReducedCrossTalkEnabled() {
}
/**
* The number of minutes to retain {@link Session} objects in DESTROYED state while waiting
* for delete replication to occur if reduced cross-talk is enabled.
*/
public long getReducedCrosstalkPurgeDelay() {
return reducedCrosstalkPurgeDelay;
}
/**
* This method checks if Internal session is already present locally
*
* @param sid
* @return a boolean
*/
return isPresent;
}
/**
* Checks whether current session should be considered local (so that local
* invocations of SessionService methods are to be used) and if local and
* Session Failover is enabled will recover the Session if the Session is
* not found locally.
*
* @return a boolean
*/
if (isSessionPresent(sid)) {
return true;
} else {
if (isSessionFailoverEnabled) {
if (isLocalServer(hostServerID)) {
throw new SessionException(SessionBundle
.getString("sessionNotObtained"));
}
return true;
}
} else {
}
}
return false;
}
/**
* Returns true if URL is the URL of the local session service.
*
* @param url the url to check
* @return true if the url represents the local session service.
*/
}
/**
* Returns true if the url is the URL of the local notification service.
*
* @param url the url to check
* @return true if the url represents the local notification service.
*/
try {
} catch (URLNotFoundException ex) {
return false;
}
}
}
/**
* Checks if server instance identified by serverID is the same as local
* instance
*
* @param serverID server id
* @return true if serverID is the same as local instance, false otherwise
*/
// TODO it appears that in non-failover mode
// thisSessionServerID == sessionServerID
// so we could do away without the if()
if (isSiteEnabled) {
} else {
}
}
/**
* Checks if server instance identified by serverID is the same as local
* instance
*
* @param sid server id
* @return true if serverID is the same as local instance, false otherwise
*/
}
/**
* This method is called by Session.getSessionServiceURL, when routing a request to an individual session host. In
* this case, the SessionID.PRIMARY_ID extension is obtained from the SessionID instance (which corresponds to the
* AM-instance host of the session). WebtopNaming will then be called to turn this serverId (01,02, etc) into a
* URL which will point a PLL client GetSession request. Calling this method is part of insuring that the PLL GetSession
* request does not get routed to a site (load-balancer).
* @param serverId the server id (PRIMARY_ID) pulled from a presented cookie.
* @return true if the specified serverId is actually a site identifier for the current deployment
*/
return sessionServerID.equals(serverId) || ((secondaryServerIDs != null) && secondaryServerIDs.contains(serverId));
}
/**
* Returns the local server ID
*
* @return The local server ID
*/
public String getLocalServerID() {
if (isSiteEnabled) {
return thisSessionServerID;
} else {
return sessionServerID;
}
}
/**
* Returns the Internal Session corresponding to a Session ID.
*
* @param sid Session Id
*/
return null;
// check if sid is actually a handle return null
// (in order to prevent from assuming recovery case)
if (isSessionHandle(sid)) {
return null;
}
return is;
}
/**
* Checks if the provided SessionID actually represents a session handle.
*
* @param sid A SessionID that may represent a standard session id or a session handle.
* @return true if SessionID is actually a session handle.
*/
}
/**
* Quick access to the total size of the remoteSessionSet.
*
* @return the size of the sessionTable
*/
public int getRemoteSessionCount() {
return remoteSessionSet.size();
}
/**
* Quick access to the total size of the sessionTable (internal sessions), including
* both invalid and valid tokens in the count, as well as 'hidden' sessions used by OpenAM itself.
*
* @return the size of the sessionTable
*/
public int getInternalSessionCount() {
return sessionTable.size();
}
/**
* Returns the Internal Session corresponding to a session handle.
*
* @param shandle Session Id
* @return Internal Session corresponding to a session handle
*/
}
/**
* As opposed to locateSession() this one accepts normal or restricted token
* This is expected to be only called once the session is detected as local
*
* @param token
* @return
*/
throws SessionException {
}
throw new SessionException(SessionBundle
.getString("invalidSessionID")
}
return sess;
}
boolean checkRestriction) throws SessionException {
return null;
// orphaned restricted token
return null;
}
if (checkRestriction) {
try {
}
} catch (SessionException se) {
throw se;
} catch (Exception e) {
throw new SessionException(e);
}
}
return session;
}
/**
* Get all valid Internal Sessions.
*/
synchronized (sessionTable) {
while (enumerator.hasMoreElements()) {
.nextElement();
}
}
}
}
return sessions;
}
/**
* Get all valid Internal Sessions matched with pattern.
*/
throws SessionException {
pattern = "*";
}
try {
if (!matchAll) {
// For application sessions, the client ID
// will not be in the DN format but just uid.
sess.getClientID();
continue;
} else {
}
continue;
}
}
break;
}
break;
}
}
} catch (Exception e) {
+ "Unable to get Session Information ", e);
throw new SessionException(e);
}
return sessions;
}
/**
* Destroy a Internal Session, whose session id has been specified.
*
* @param sid
*/
sess.setIsISStored(false);
}
}
}
/**
* Logout a Internal Session, whose session id has been specified.
*
* @param sid
*/
sess.setIsISStored(false);
}
}
}
/**
* Simplifies the signalling that a Session has been removed.
* @param session Non null InternalSession.
* @param event An integrate from the SessionEvent class.
*/
}
/**
* Decrements number of active sessions
*/
public static synchronized void decrementActiveSessions() {
// Fix for OPENAM-486: this is a sanity-check for sessioncount, so it
// can't go below zero any more in case of erroneous behavior..
if (numberOfActiveSessions > 0) {
}
}
}
/**
* Increments number of active sessions
*/
public static synchronized void incrementActiveSessions() {
if (SystemProperties.isServerMode()) {
if (MonitoringUtil.isRunning()) {
}
}
}
/**
* Returns number of active sessions
*/
public static synchronized int getActiveSessions() {
return numberOfActiveSessions;
}
/*
* Returns current Notification queue size.
*/
public static int getNotificationQueueSize() {
return threadPool.getCurrentSize();
}
/**
* Add a listener to a Internal Session.
*
* @param session - InternalSession Object
* @param url
* @param sid sid to be used with notification (master or restricted)
*/
throw new IllegalArgumentException("Session id mismatch");
}
}
}
/**
* Add a listener to all Internal Sessions.
*
* @param url
*/
}
}
// The following methods are corresponding to the session requests
// defined in the Session DTD. Those methods are being called
// in SessionRequestHandler class
/**
* Returns the Session information.
*
* @param sid
* @param reset
* @throws SessionException
*/
throws SessionException {
if (reset) {
}
return info;
}
/**
* Gets all valid Internal Sessions, depending on the value of the user's
* preferences.
*
* @param s
* @throws SessionException
*/
throws SessionException {
int status[] = {0};
}
/**
* Gets all valid Internal Sessions, depending on the value of the user's
* preferences.
*
* @param s
* @throws SessionException
*/
throws SessionException {
throw new SessionException(SessionBundle
.getString("invalidSessionState")
}
try {
"iplanet-am-session-get-valid-sessions");
}
// top level admin gets all sessions
boolean isTopLevelAdmin = hasTopLevelAdminRole(s);
// replace session id with session handle to prevent from
// impersonation
}
}
return infos;
} catch (Exception e) {
throw new SessionException(e);
}
}
/**
* Destroy a Internal Session, depending on the value of the user's
* preferences.
*
* @param requester
* @param sid
* @throws SessionException
*/
return;
}
// let us check if the argument is a session handle
}
}
}
/**
* Checks if the requester has the necessary permission to destroy the provided session. The user has the necessary
* privileges if one of these conditions is fulfilled:
* <ul>
* <li>The requester attempts to destroy its own session.</li>
* <li>The requester has top level admin role (having read/write access to any service configuration in the top
* level realm).</li>
* <li>The session's client domain is listed in the requester's profile under the
* <code>iplanet-am-session-destroy-sessions service</code> service attribute.</li>
* </ul>
*
* @param requester The requester's session.
* @param sid The session to destroy.
* @throws SessionException If none of the conditions above is fulfilled, i.e. when the requester does not have the
* necessary permissions to destroy the session.
*/
public void checkPermissionToDestroySession(Session requester, SessionID sid) throws SessionException {
}
try {
// a session can destroy itself or super admin can destroy anyone including another super admin
return;
}
}
} catch (Exception e) {
throw new SessionException(e);
}
}
/**
* Logout the user.
*
* @param sid
* @throws SessionException
*/
}
//if the provided sid was a restricted token, resolveToken will always validate the restriction, so there is no
//need to check restrictions here.
}
/**
* Adds listener to a Internal Sessions.
*
* @param sid Session ID
* @param url
* @throws SessionException Session is null OR the Session is invalid
*/
throws SessionException {
throw new SessionException(SessionBundle
.getString("invalidSessionState")
}
}
/**
* Add a listener to all Internal Sessions.
*
* @param s
* @param url
* @throws SessionException
*/
throws SessionException {
throw new SessionException(SessionBundle
.getString("invalidSessionState")
}
return;
}
try {
"iplanet-am-session-add-session-listener-on-all-sessions");
null);
}
} catch (Exception e) {
throw new SessionException(e);
}
}
/**
* Sets internal property to the Internal Session.
*
* @param sid
* @param name
* @param value
* @throws SessionException
*/
throws SessionException {
}
/**
* Given a restricted token, returns the SSOTokenID of the master token
* can only be used if the requester is an app token
*
* @param s Must be an app token
* @param restrictedID The SSOTokenID of the restricted token
* @return The SSOTokenID string of the master token
* @throws SSOException If the master token cannot be dereferenced
*/
throws SessionException {
// we need to accomodate session failover situation
//first try
if (!isLocalServer(hostServerID)) {
}
if (!isLocalServer(hostServerID)) {
//TODO consider one retry attempt
} else {
return masterID;
}
}
}
}
}
// sjf bug 6797573
try {
conn.setDoOutput(true);
return null;
}
} finally {
}
return null;
}
/**
* Sets external property in the Internal Session as long as it is not
* protected
*
* @param clientToken - Token of the client setting external property.
* @param sid
* @param name
* @param value
* @throws SessionException
*/
throws SessionException {
}
/**
* Sends the Internal Session event to the SessionNotificationSender.
*
* @param sess Internal Session.
* @param evttype Event Type.
*/
try {
// First send local notification. sendToLocal will return
// true if remote URL's exists than add the notification
// to the thread pool to process remote notifications.
if (sns.sendToLocal()) {
}
} catch (ThreadPoolException e) {
}
}
/**
* Logs the Internal Session Events.
*
* @param sess Internal Session
* @param eventType event type.
*/
switch (eventType) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
default:
break;
}
}
}
return logger;
}
private LogMessageProvider getLogMessageProvider()
throws Exception {
if (logProvider == null) {
}
return logProvider;
}
if (!logStatus) {
return;
}
try {
uidData = "N/A";
} else {
}
"Cannot write to the session log file: ", ex);
}
}
if (!logStatus) {
return;
}
if (errorLogger == null) {
}
try {
data,
null);
"Cannot write to the session error " +
"log file: ", ex);
}
}
if (ssoManager == null) {
}
return ssoManager;
}
}
if (adminToken == null) {
return adminToken;
}
return adminToken;
}
/**
* Private Singleton Session Service.
*/
private SessionService() {
// Initialise all CTS fields.
try {
.doPrivileged(new AdminDNAction());
.doPrivileged(new AdminPasswordAction());
/*
* We need to get the session server unique id from the platform
* server list. Naming table has all the platform servers and the
* unique key mappings for each server. We will append this server
* id as part of the session id so that naming service will use this
* id to find the respective session while decrypting the session id
*/
if (isSiteEnabled) {
secondaryServerIDs = new HashSet();
if (secondaryIDs != null) {
while (st.hasMoreTokens()) {
}
} else {
}
}
} else {
}
// Obtain the secureRandom instance
try {
} catch (NoSuchProviderException e) {
}
}
/*
* In session failover mode we need to distinguish between server
* instance own address and the cluster address We will use new set
* of properties
*
* com.iplanet.am.localserver.{protocol,host,port}
*
* to point to this instance while existing properties
*
* com.iplanet.am.server.{protocol,host,port}
*
* will point to the load balancer address
*/
/**
* Provide "this" Instance Variables available for either
* single instance or a site enabled instance.
*
* ** Special note the "this" instance or self Globals,
* must be initialized prior to the PostInit() phase,
* otherwise Null Pointer Exceptions will occur when
* referencing these variables during postInit.
*
*/
if ((thisSessionServerProtocol == null) ||
(thisSessionServerPortAsString == null) ||
(thisSessionServer == null) ||
(thisSessionURI == null)
) {
"propertyMustBeSet", null);
}
/*
* Initialize global session parameters. do not move this method to
* any other place.
*/
postInit();
"SessionService.SessionService(): Initialization Failed",
ex);
}
} // End of private single constructor.
/**
* Initialise the ClusterStateService to monitor all Servers within the current
* Site, and also all Sites except the current Site. This will allow the
* ClusterStateService to provide an answer to whether a Site is up or down.
*
* @throws Exception If there was an unexpected error during initialisation.
*/
private synchronized void initializationClusterService() throws Exception {
try {
} catch (Exception e) {
+ ", using default");
}
try {
} catch (Exception e) {
+ Constants.
+ ", using default");
}
// Initialize Our Cluster State Service
// Ensure we place our Server in Member Map.
// Collect all Sites to monitor
// Excluding the current local site, as it is not needed for intra-cluster failover.
if (isLocalSite(serverOrSiteId)) {
continue;
}
}
}
// Instantiate the State Service.
clusterStateService = new ClusterStateService(this, thisSessionServerID, timeout, period, clusterMemberMap, siteMemberMap);
// Show our State Server Info Map
if (sessionDebug.messageEnabled())
}
}
/**
* This is a key method for "internal request routing" mode It determines
* the server id which is currently hosting session identified by sid. In
* "internal request routing" mode, this method also has a side effect of
* releasing a session which no longer "belongs locally" (e.g., due to
* primary server instance restart)
*
* @param sid session id
* @return server id for the server instance determined to be the current
* host
* @throws SessionException
*/
if (!isSessionFailoverEnabled) {
return sid.getSessionServerID();
} else {
if (getUseInternalRequestRouting()) {
return sid.getSessionServerID();
}
// if we happen to have local session replica
// get rid of it, as hosting server instance
// is not supposed to be local
if (!isLocalServer(serverID)) {
// actively clean up duplicates
}
return serverID;
} else {
return sid.getSessionServerID();
}
}
}
/**
* Determines current hosting server instance for internal request routing
* mode.
*
* @param sid session id
* @return server id for the server instance determined to be the current
* host
* @throws SessionException
*/
throws SessionException {
// if this is our local Server
return serverID;
}
// if session is from remote site
return serverID;
}
// Ensure we have a Cluster State Service Available.
synchronized (this) {
if (clusterStateService == null) {
try {
} catch (Exception e) {
sessionDebug.error("Unable to Initialize the Cluster Service, please review Configuration settings.", e);
throw new SessionException(e);
}
}
}
// Check for Service Available.
return primaryID;
} else {
for (int i = 0; i < selectionListSize; ++i) {
.itemAt(i));
if (selectedServerId == null) {
continue;
}
break;
}
}
// since current server is also part of the selection list
// selection process is guaranteed to succeed
return selectedServerId;
}
}
/**
* Indicates whether server is running in "internal request routing" mode
*
* @return true if internal request routing is enabled, false otherwise
*/
static public boolean getUseInternalRequestRouting() {
if (isSessionFailoverEnabled) {
return useInternalRequestRouting;
}
return false;
}
static public void setSessionTrimmingEnabled(boolean value) {
if (sessionDebug.messageEnabled()) {
}
}
static public boolean isSessionTrimmingEnabled() {
return isSessionTrimmingEnabled;
}
static public void setSessionConstraintEnabled(boolean value) {
}
static public boolean isSessionConstraintEnabled() {
return isSessionConstraintEnabled;
}
static public void setDenyLoginIfDBIsDown(boolean value) {
}
static public boolean denyLoginIfDBIsDown() {
return denyLoginIfDBIsDown;
}
public static String getConstraintHandler() {
return constraintHandler;
}
}
/**
* Utility helper method to obtain session repository reference
*
* @return reference to session repository
*/
protected static CTSPersistentStore getRepository() {
if (!getUseInternalRequestRouting()) {
return null;
}
if (coreTokenService == null) {
}
return coreTokenService;
}
/**
* Actively check if server identified by serverID is up
*
* @param serverID server id
* @return true if server is up, false otherwise
*/
return ((serverID == null) || (serverID.isEmpty())) ? false : clusterStateService.checkServerUp(serverID);
}
/**
* Indicates that the Site is up.
*
* @param siteId A possibly null Site Id.
* @return True if the Site is up, False if it failed to respond to a query.
*/
}
/**
* Post Initialization
*/
private void postInit() {
try {
isPropertyNotificationEnabled = true;
}
isSessionTrimmingEnabled = true;
}
if (sessionDebug.messageEnabled()) {
" isSessionTrimmingEnabled=" + isSessionTrimmingEnabled);
}
isSessionConstraintEnabled = true;
}
if (sessionDebug.messageEnabled()) {
}
DENY_LOGIN_IF_DB_IS_DOWN, "NO");
denyLoginIfDBIsDown = true;
}
if (sessionDebug.messageEnabled()) {
"denyLoginIfDBIsDown=" + denyLoginIfDBIsDown);
}
if (sessionDebug.messageEnabled()) {
+ "quota exhausted:" + constraintHandler);
}
MAX_WAIT_TIME_FOR_CONSTARINT, "6000"));
/* in OpenSSO 8.0, we have switched to create sub
* configuration with
* site name. hence we need to lookup the site name based on the URL
*/
CoreTokenConstants.IS_SFO_ENABLED, false);
// Currently, we are not allowing to default to Session Failover HA,
// even with a single server to enable session persistence.
// But can easily be turned on in the Session SubConfig.
if (sfoEnabled) {
isSessionFailoverEnabled = true;
useRemoteSaveMethod = true;
useInternalRequestRouting = true;
// Determine whether crosstalk is enabled or disabled (default to false in SFO case).
if (isReducedCrosstalkEnabled) {
}
// Obtain Site Ids
}
// Next, must be in this Order!
// Initialize our Cluster Member Map, first!
// Initialize the Cluster State Service, second!
// (As Cluster Service uses Cluster Member Map).
// ************************************************************************
// Now Bootstrap CoreTokenService Implementation, if one was specified.
if (coreTokenService == null) {
// Instantiate our Session Repository Implementation.
// Allows Static Elements to Initialize.
}
} // End of sfoEnabled check.
} // End of Sub-Configuration Existence check.
if (sessionDebug.messageEnabled()) {
}
+ "Unable to get Session Schema Information", ex);
}
}
/**
* This method will execute all the globally set session timeout handlers
* with the corresponding timeout event simultaniously.
*
* @param sessionId The timed out sessions ID
* @param changeType Type of the timeout event: IDLE_TIMEOUT (1) or MAX_TIMEOUT (2)
*/
// Take snapshot of reference to ensure atomicity.
try {
public void run() {
try {
SessionTimeoutHandler.class).newInstance();
switch (changeType) {
case SessionEvent.IDLE_TIMEOUT:
break;
case SessionEvent.MAX_TIMEOUT:
break;
}
if (Thread.interrupted()
|| ex instanceof InterruptedException
|| ex instanceof InterruptedIOException) {
} else {
}
} finally {
}
}
};
}
// Wait 1000ms for all handlers to complete.
try {
} catch (InterruptedException ignored) {
// This should never happen: we can't handle it here, so propagate it.
}
// It doesn't matter really if the future completes between isDone and cancel.
}
}
} catch (SSOException ssoe) {
}
}
}
/**
* Initialize the cluster server map given the server IDs in Set (AM70).
*/
continue;
}
// ************************************************************
// This if Clause is very important, please do not think it is
// not. If we pollute the cluster map with duplicate URLs
// There is a very good chance Login processing will
// automatically fail, since it can not determine
// which serverId is which.
// Only Associate one Server URL to a Single ServerID.
// @since 10.1
//
// Further investigate, as we should not get duplicates here...
//
}
}
/**
* Initialize the cluster server map given the server IDs in Set.
* Invoked by NamingService whenever any global configuration changes occur.
*/
public void ReInitClusterMemberMap() throws Exception {
if (isSessionFailoverEnabled()) {
}
}
if (sessionDebug.messageEnabled()) {
}
}
/**
* A convenience method to get the cluster server list in delimiter
* separated String format. This is currently used in message debug log.
*/
private String getClusterServerList() {
}
return clusterServerList.toString();
}
/**
*/
public SessionBroadcastMode getLogoutDestroyBroadcast() {
return logoutDestroyBroadcast;
}
/**
* Inner Session Notification Publisher Class Thread.
*/
class SessionNotificationSender implements Runnable {
private InternalSession session;
private int eventType;
}
/**
* returns true if remote URL exists else returns false.
*/
boolean sendToLocal() {
boolean remoteURLExists = false;
// Check global URLs first
if (!sessionEventURLs.isEmpty()) {
while (globalUrls.hasMoreElements()) {
try {
// If the Global notification is processed successfully
// than no need to send individual notification.
} else {
// If the Global notification is for a remote URL, it should be handled from run()
// - This allows remote notification to be handled asynchronously from another thread
remoteURLExists = true;
}
} catch (Exception e) {
}
}
}
// The check individual URLs
try {
if (isLocalNotificationService(parsedUrl)) {
}
} else {
// If the Global notification is for a remote URL, it should be handled from run()
// - This allows remote notification to be handled asynchronously from another thread
remoteURLExists = true;
}
} catch (Exception e) {
}
}
}
return remoteURLExists;
}
/**
* Thread which sends the Session Notification.
*/
public void run() {
throw new IllegalStateException("Must call sendToLocal before starting thread");
}
// Check global URLs first
if (!sessionEventURLs.isEmpty()) {
while (globalUrls.hasMoreElements()) {
try {
// Only send to remote URLs, local URLs should be handled by sendToLocal
}
} catch (Exception e) {
}
}
}
// The check individual URLs
try {
// Only send to remote URLs, local URLs should be handled by sendToLocal
if (!isLocalNotificationService(parsedUrl)) {
}
}
} catch (Exception e) {
}
}
}
}
} // End of SessionNotificationSender Inner Class.
/**
* Returns the User of the Session
*
* @param s Session
* @throws SessionException
* @throws SSOException
*/
throws SessionException, SSOException {
try {
} catch (IdRepoException e) {
"SessionService: failed to get the user's identity object", e);
}
return user;
}
/**
* Returns true if the user has top level admin role
*
* @param s Session.
* @throws SessionException
* @throws SSOException
*/
private boolean hasTopLevelAdminRole(Session s)
throws SessionException, SSOException {
}
/**
* Returns true if the user has top level admin role
*
* @param tokenUsedForSearch Single Sign on token used to do the search.
* @param clientID Client ID of the login user.
* @throws SessionException
* @throws SSOException
*/
private boolean hasTopLevelAdminRole(
) throws SessionException, SSOException {
boolean topLevelAdmin = false;
try {
} catch (DelegationException de) {
"failed to check the delegation permission.", de);
}
return topLevelAdmin;
}
/**
* Returns true if the user is super user
*
* @param uuid the uuid of the login user
*/
boolean isSuperUser = false;
try {
// Get the AMIdentity Object for super user
}
//Get the AMIdentity Object for login user
//Check for the equality
} catch (SSOException ssoe) {
"Cannot get the admin token for this operation.");
} catch (IdRepoException idme) {
"Cannot get the user identity.");
}
if (sessionDebug.messageEnabled()) {
+ isSuperUser);
}
return isSuperUser;
}
/**
* Creates InternalSession which is always coupled with Http session This is
* only used in session failover mode to ensure that every internal session
* is associated with Http session used as fail-over store
*
* @param domain authentication domain passed to newInternalSession
*/
try {
}
+ "/GetHttpSession" + query);
return null;
}
} finally {
}
return null;
}
/**
* This functions invalidates the http session associated with identity
* session specified by sid
*
* @param sid
* @return
*/
return true;
}
try {
+ query);
} catch (ConnectException ex) {
if (sessionDebug.messageEnabled()) {
.message("invalidateHttpSesion: failed to connect to "
+ url);
}
return true;
} finally {
}
return false;
}
/**
* Helper function to handle stream closure and associated exceptions
*/
try {
} catch (IOException e) {
}
}
}
/**
* Helper function to handle stream closure and associated exceptions
*/
try {
} catch (IOException e) {
}
}
}
/**
* Removes InternalSession from the session table so that another server
* instance can be an owner This is used to help work around persistent
* association of OpenSSO session id and Http session id which
* some loadbalancers (like Weblogic) use to make routing decisions. This
* helps to deal with the case a session is migrated while the current owner
* is still alive to avoid having redundant copies of the session. This is
* the client side of distributed invocation
*
* @param owner url of the server instance who previously owned the session
* @param sid session id of the session migrated
*/
if (sessionDebug.messageEnabled()) {
+ " from server instance: " + owner);
}
try {
} catch (ConnectException ex) {
if (sessionDebug.messageEnabled()) {
+ url);
}
return true;
} finally {
}
return false;
}
/**
* Removes InternalSession from the session table so that another server
* instance can be an owner This is the server side of distributed
* invocation initiated by calling releaseSession()
*
* @param sid session id of the session migrated
*/
if (!isSessionFailoverEnabled) {
}
// switch to non-local mode for cached cient side session
// image
} else {
if (sessionDebug.messageEnabled()) {
+ sid);
}
}
return HttpURLConnection.HTTP_OK;
}
/**
* If InternalSession is not present, we attempt to recover its state from
* associated HttpSession. We have to set the session tracking cookie to
* HttpID which is present in the SessionID object. This will work in the
* fail over cases. We first get the HttpSession by invoking the
* GetHttpSession Servlet on the SAME server instance this code is invoked.
* This should trigger the Web container to perform recovery of the
* associated Http session
* <p/>
* We also pass the SessionID to the servlet to double check the match
* between the session id and Http session
* <p/>
* This is the "client side" of the remote invocation. The servlet will call
* retrieveSession() to complete the work
*
* @param sid Session ID
*/
if (!isSessionFailoverEnabled) {
return null;
}
if (getUseInternalRequestRouting()) {
try {
return sess;
}
/**
* As a side effect of deserialising an InternalSession, we must trigger
* the InternalSession to reschedule its timing task to ensure it
* maintains the session expiry function.
*/
sess.setSessionService(this);
} catch (CoreTokenException e) {
}
return sess;
} else {
if (sessionDebug.messageEnabled()) {
.message("Recovering InternalSession from HttpSession: "
+ sid);
}
try {
+ "/GetHttpSession" + query);
}
} finally {
}
return sess;
}
}
/**
* This is the "server side" of the remote invocation for recoverSession()
* It is being called by GetHttpSession servlet to complete the work
* <p/>
* If recovery is possible we need to first notify existing server instance
* "owning" the session (if any) to release the session instance otherwise
* we end up with duplicates
*
* @param sid Session ID
*/
if (sessionState == null) {
.message("GISFHS-No InternalSession in HttpSession");
return null;
} else {
return null;
}
// tell all previously registered owners of this session
// who might still potentially have a copy to release it
// (we do not expect a session to migrate more than one
// time so typically this list will have a size of 2).
}
continue;
return null;
}
}
// add current server to the list of former owners
return sess;
}
}
return null;
}
/**
* Utility used to updated various cross-reference mapping data structures
* associated with sessions up-to-date when sessions are being recovered
* after server instance failure
*
* @param sess session object
*/
return;
if (checkIfShouldDestroy(sess))
return;
if (getUseInternalRequestRouting()) {
if (!isLocalServer(primaryID)) {
}
}
if (sessionHandle != null) {
}
}
}
/**
* function to remove remote sessions when primary server is up
*/
public void cleanUpRemoteSessions() {
if (getUseInternalRequestRouting()) {
synchronized (remoteSessionSet) {
// getCurrentHostServer automatically releases local
// session replica if it does not belong locally
try {
}
// if session does not belong locally remove it
if (!isLocalServer(hostServer)) {
}
}
}
}
}
/**
* Utility method to check if session has to be destroyed and to remove it
* if so Note that contrary to the name sess.shouldDestroy() has non-trivial
* side effects of changing session state and sending notification messages!
*
* @param sess session object
* @return true if session should (and has !) been destroyed
*/
boolean shouldDestroy = false;
try {
shouldDestroy = true;
}
if (shouldDestroy) {
try {
}
}
return shouldDestroy;
}
/**
* This is used to save session state using remote method rather than
* directly using a reference to HttpSession saved in the InternalSession
* which is not guaranteed to survive across http requests. We set the
* session tracking cookie to HttpID which is present in the SessionID
* object. We first get the HttpSession by invoking the GetHttpSession
* Servlet on the SAME server instance this code is invoked. This should
* trigger the Web container to provide a valid instance of the associated
* Http session and then use it to save the session
* <p/>
* This is the "client side" of the remote invocation. The servlet will call
* handleSaveSession() to complete the work
*
* @param sid Session ID
*/
if (!isSessionFailoverEnabled) {
return false;
}
if (sessionDebug.messageEnabled()) {
+ sid);
}
try {
+ query);
} finally {
}
return false;
}
/**
* This is the "server side" of the remote invocation for saveSession() It
* is being called by GetHttpSession servlet to complete the work
*
* @param sid master Session ID
* @param httpSession http session
*/
if (!isSessionFailoverEnabled) {
}
return HttpURLConnection.HTTP_NOT_FOUND;
}
"handleSaveSession: http session id does not match sid "
+ sid);
return HttpURLConnection.HTTP_INTERNAL_ERROR;
}
return HttpURLConnection.HTTP_OK;
}
/**
* This method is used to create restricted token
*
* @param owner server instance URL
* @param masterSid SessionID
* @param restriction restriction
*/
try {
conn.setDoOutput(true);
return null;
}
} finally {
}
return null;
}
/**
* This method is the "server side" of the getRestrictedTokenIdRemotely()
*
* @param masterSid SessionID
* @param restriction restriction
*/
try {
}
return null;
}
/**
* This method is used to update the HttpSession when InternalSession
* property changes.
*
* @param session Session Object
*/
if (!isSessionFailoverEnabled) {
return;
}
if (getUseInternalRequestRouting()) {
// do not save sessions which never expire
if (!session.willExpire()) {
return;
}
try {
} catch (Exception e) {
"exception encountered", e);
// handleReleaseSession(session.getID());
}
} else {
if (useRemoteSaveMethod) {
} else {
if (httpSession != null) {
}
}
}
}
/**
* Save identity session state in associated http session
*
* @param session
* @param httpSession
*/
try {
} catch (Exception e) {
"SessionService.doSaveSession: exception encountered", e);
}
}
/**
* Helper function used for remote invocation over HTTP It constructs
* HttpURLConnection using url and adding cookies based on sid and returns
* it to the caller. In order to complete the invocation caller is supposed
* to open input stream
*
* @param url url
* @param sid SessionID
*/
if (!isSessionFailoverEnabled) {
return null;
}
try {
if (extraCookies != null) {
}
}
}
if (sessionDebug.messageEnabled()) {
}
connection.setDoInput(true);
throw ex;
}
return connection;
}
/**
* This method is used to encrypt the InternalSession object before storing
* into HttpSession.
*
* @param obj Object to be encrypted
*/
try {
byteOut = new ByteArrayOutputStream();
// convert object to byte using streams
// convert byte to string
// encrypt string
.getHardcodedKeyEncryptor()));
} catch (Exception e) {
.message("Error in encrypting the Internal Session object");
return null;
}
return strEncrypted;
}
/**
* This method is used to decrypt the InternalSession object, after
* obtaining from HttpSession.
*
* @param strEncrypted Object to be decrypted
*/
if (strEncrypted == null)
return null;
byte byteDecrypted[] = null;
try {
// decrypt string
.getHardcodedKeyEncryptor()));
// convert string to byte
// convert byte to object using streams
} catch (Exception e) {
.message("Error in decrypting the Internal Session object"
+ e.getMessage());
return null;
}
if (tempObject == null) {
return null;
}
return (InternalSession) tempObject;
}
/**
* This method is used to encode the SessionID.
*
* @param decodedStr Decoded String
*/
if (decodedStr == null)
return null;
if (decoded[i] == '%')
numberOfChar++;
}
encoded = new char[finalLength];
switch (decoded[i]) {
case '%':
encoded[j] = '%';
j = j + 3;
break;
default:
j++;
break;
}
}
}
/**
* This is uia very useful method for debuggin the InternalSession values.
* It can be deleted later.
*/
.message("InternalSession Attribute is NULL in -->"
+ calledFrom);
return;
}
try {
+ " --Value of maxSessionTime-->"
+ is.getMaxSessionTime());
+ " --Value of property Name is -->"
} else {
+ " --Value of property Name is NULL");
}
} catch (Exception e) {
+ e.getMessage());
}
} else {
}
}
/**
* Returns true if the given pattern is contained in the string.
*
* @param string to examine
* @param pattern to match
* @return true if string matches <code>filter</code>
*/
return true;
}
if (wildCardIndex >= 0) {
return false;
}
int stringIndex = 0;
if (wildCardIndex > 0) {
}
return false;
}
}
return true;
}
}
return false;
}
/**
* @return Returns the connectionMaxWaitTime.
*/
public static int getConnectionMaxWaitTime() {
return connectionMaxWaitTime;
}
public static void setMaxWaitTimeForConstraint(int value) {
}
/**
* @return Returns the maxWaitTimeForConstraint.
*/
public static int getMaxWaitTimeForConstraint() {
return maxWaitTimeForConstraint;
}
/**
* @return Returns the sessionExternalRepositoryURL.
*/
public static String getSessionExternalRepositoryURL() {
return sessionExternalRepositoryURL;
}
/**
* @return Returns the sessionStorePassword.
*/
public static String getSessionStoreUserName() {
return sessionStoreUserName;
}
/**
* @return Returns the sessionStorePassword.
*/
public static String getSessionStorePassword() {
return sessionStorePassword;
}
public boolean isSiteEnabled() {
return isSiteEnabled;
}
/**
* @return Returns true if Property change notifications are enabled.
*/
public static boolean isPropertyNotificationEnabled() {
return isPropertyNotificationEnabled;
}
protected static void setPropertyNotificationEnabled(boolean value) {
}
if (!isPropertyNotificationEnabled) {
return false;
}
}
protected static Set getNotificationProperties() {
return notificationProperties;
}
}
} else {
}
}
// TODO check if restructuring of interface between Auth and Session
// can eliminate the need for this utility class
static public class ExtendedSessionID extends SessionID {
super(sid);
}
}
}