Session.java revision 26304a2a091af368cfc16c977bcce6d17195360a
/*
* 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: Session.java,v 1.25 2009/08/14 17:53:35 weisun2 Exp $
*
* Portions copyright 2010-2016 ForgeRock AS.
*/
/**
* The <code>Session</code> class represents a session. It contains session
* related information such as session ID, session type (user/application),
* client ID (user ID or application ID), session idle time, time left on the
* session, and session state. It also allows applications to add listener for
* session events.
*
* <pre>
* The following is the state diagram for a session:
*
* |
* |
* |
* V
* ---------- invalid
* | |
* | |creation (authentication OK)
* | |
* |max login time| max idle time
* |destroy V --------------->
* | valid inactive --
* | | <-------------- |
* | | reactivate |
* | | |
* | | logout | destroy
* | | destroy | max session time
* | | max session time |
* | V |
* ---------> destroy <---------------------
*
* </pre>
*
* @see com.iplanet.dpro.session.SessionID
* @see com.iplanet.dpro.session.SessionListener
*/
/**
* Defines the type of Session that has been created. Where 0 for User
* Session; and 1 for Application Session.
*/
private int sessionType;
/**
* Identification string of the Client using this Session.
*/
/**
* Authenticated.
*/
private String clientDomain;
/**
* Total Maximum time allowed for the session, in minutes.
*/
private long maxSessionTime;
/**
* Maximum idle time allowed for the session, in minutes.
*/
private long maxIdleTime;
/**
* Maximum time for which the cached session is used, in minutes.
*/
private long maxCachingTime;
/**
* The time for which the session has been idle, in seconds.
*/
private long sessionIdleTime;
/**
* Total time left for the session, in seconds.
*/
private long sessionTimeLeft;
/**
* Time after which the session will be considered invalid, in milliseconds.
*/
private long sessionExpiryTime;
/*
* This is the time value (computed as System.currentTimeMillis()/1000) when
* the session timed out. Value zero means the session has not timed out.
*/
private volatile long timedOutAt = 0;
/**
* Four possible values for the state of the session:
* <ul>
* <li>0 - Invalid</li>
* <li>1 - Valid</li>
* <li>2 - Inactive</li>
* <li>3 - Destroyed</li>
* </ul>
*/
protected int sessionState;
/**
* This is the time value (computed as System.currentTimeMillis()) when a DESTROYED
* session should be removed from the {@link SessionCache#sessionTable}.
*
* It will be set to {@link com.iplanet.dpro.session.service.SessionService#getReducedCrosstalkPurgeDelay() }
* minutes after the time {@link org.forgerock.openam.session.SessionCache#removeRemoteSID } is called.
*
* Value zero means the session has not been destroyed or cross-talk is not being reduced.
*/
private volatile long purgeAt = 0;
/**
* If this is a Remote session that has been destroyed but not yet removed from the
* sessionTable, this flag is used to avoid repeated notification of the DESTROY event
* to session listeners.
*/
/**
* All session related properties are stored as key-value pair in this
* table.
*/
/**
* URL of the Session Server, where this session resides.
*/
private URL sessionServiceURL;
/**
* Type of the Event
*/
private int eventType = -1;
/**
* Last time the client sent a request associated with this session, as the
* number of seconds since midnight January 1, 1970 GMT.
*/
private volatile long latestRefreshTime;
/**
* Indicates whether the latest access time need to be reset on the session.
*/
volatile boolean needToReset = false;
/**
* Indicates whether to use local or remote calls to Session Service
*/
protected boolean sessionIsLocal = false;
// Debug instance
/**
* Set of session event listeners for THIS session only
*/
/**
* This is used only in polling mode to find the polling state of this
* session.
*/
private volatile boolean isPolling = false;
private final SessionCookies sessionCookies;
private final SessionPollerPool sessionPollerPool;
private final SessionCache sessionCache;
private final SessionPLLSender sessionPLLSender;
private final SessionServiceURLService sessionServiceURLService;
private final SessionOperationStrategy sessionOperationStrategy;
private final SessionService sessionService;
/**
* Constructor used by this package only.
*
* ClientSDK: This code has to operate both on the serer and the client. It needs
* to be able to resolve dependencies in an appropriate way in both conditions.
*/
if (SystemProperties.isServerMode()) {
// Had to choose, final or @Inject approach. Went final.
} else {
sessionOperationStrategy = new RemoteSessionOperationStrategy(new RemoteOperations(sessionDebug, requests));
}
}
this(sid);
this.sessionIsLocal = sessionIsLocal;
}
public long getPurgeAt() {
return purgeAt;
}
public void setPurgeAt(long purgeAt) {
}
public void setSessionIsLocal(boolean sessionIsLocal) {
this.sessionIsLocal = sessionIsLocal;
}
public AtomicBoolean getRemoved() {
return removed;
}
public SessionID getSessionID() {
return sessionID;
}
public String getCookieStr() {
return cookieStr;
}
}
//todo: Object...
}
/**
* Enables the Session Polling
* @param b if <code>true</code> polling is enabled, disabled otherwise
*/
public void setIsPolling(boolean b) {
isPolling = b;
}
/**
* Checks if Polling is enabled
* @return <code> true if polling is enabled , <code>false<code> otherwise
*/
protected boolean getIsPolling() {
return isPolling;
}
return false;
}
return false;
}
public boolean isEmpty() {
return true;
}
public long getRunPeriod() {
return -1;
}
public void run() {
if (sessionPollerPool.isPollingEnabled()) {
try {
if (!getIsPolling()) {
long expectedTime;
if (willExpire(maxIdleTime)) {
if (sessionPollerPool.getCacheBasedPolling()) {
}
} else {
}
if (expectedTime > scheduledExecutionTime()) {
// Get an instance as required otherwise it causes issues on container restart.
return;
}
sender = new SessionPollerSender(this);
}
new RestrictedTokenAction() {
try {
setIsPolling(true);
} catch (ThreadPoolException e) {
setIsPolling(false);
}
return null;
}
});
}
} catch (SessionException se) {
}
} else {
if (purgeAt > 0) {
/**
* Reduced crosstalk protection.
*
* In order to prevent sessions from being (re)created from CTS on remote servers before
* the destroyed state has been propagated, remote sessions are kept in memory for a configurable
* amount of time {@link CoreTokenConstants.REDUCED_CROSSTALK_PURGE_DELAY }.
*
* This delay introduced to cover the CTS replication lag is only required when running as an
* OpenAM server with a 'remote' copy of a session; therefore, this feature is not required
* when polling is enabled - since polling is only ever used by non-OpenAM clients.
*/
// destroyed session scheduled for purge
if (purgeAt > scheduledExecutionTime()) {
return;
}
sessionRemovalDebugMessage = "Session Removed, Reduced Crosstalk Purge Time complete";
} else {
// schedule at the max session time
long expectedTime = -1;
if (willExpire(maxSessionTime)) {
}
if (expectedTime > scheduledExecutionTime()) {
return;
}
sessionRemovalDebugMessage = "Session Destroyed, Caching time exceeded the Max Session Time";
}
try {
if (sessionDebug.messageEnabled()) {
}
}
}
}
/**
* Returns the session ID.
* @return The session ID.
*/
return sessionID;
}
/**
* Returns the session type.
*
* @return The session type.
*/
public int getType() {
return sessionType;
}
/**
* Returns the client ID in the session.
*
* @return The client ID in the session.
*/
public String getClientID() {
return clientID;
}
/**
* Returns the client domain in the session.
*
* @return The client domain in the session.
*/
public String getClientDomain() {
return clientDomain;
}
/**
* Returns the maximum session time in minutes.
*
* @return The maximum session time.
*/
public long getMaxSessionTime() {
return maxSessionTime;
}
/**
* Returns the maximum session idle time in minutes.
*
* @return The maximum session idle time.
*/
public long getMaxIdleTime() {
return maxIdleTime;
}
/**
* Returns true if the session has timed out.
* @return <code>true</code> if session timed out,
* <code>false</code>otherwise
* @exception SessionException
*/
public boolean isTimedOut() throws SessionException {
/**
* Before going to the server, check if the session has been already
* marked TimedOut or not.
*/
if (timedOutAt > 0) {
return true;
}
try {
refresh(false);
} catch (SessionTimedOutException e) {
}
}
return timedOutAt > 0;
}
/**
* Returns the extra time left(in seconds) for the client Session after the
* session timed out. If extra time left is zero, it means the session is
* ready to be removed permanently. If it returns -1 it means the session
* did not even reached the time out state.
* @return <code>Session</code> Purge time left
* @exception <code>SessionException</code>
*/
public long getTimeLeftBeforePurge() throws SessionException {
/**
* period.
*/
if (!isTimedOut()) {
return -1;
}
/**
* Return the extra time left, if the session has timed out due to
*/
}
/**
* Returns the maximum session caching time in minutes.
*
* @return The maximum session caching time.
*/
public long getMaxCachingTime() {
return maxCachingTime;
}
/**
* Returns the session idle time in seconds.
*
* @return The session idle time.
* @exception SessionException if the session reached its maximum session
* time, or the session was destroyed, or there was an error
* during communication with session service.
*/
public long getIdleTime() throws SessionException {
return sessionIdleTime;
}
/**
* Returns the time left for this session in seconds.
*
* @return The time left for this session.
* @exception SessionException is thrown if the session reached its
* maximum session time, or the session was destroyed, or
* there was an error during communication with session
* service.
*/
public long getTimeLeft() throws SessionException {
return sessionTimeLeft;
}
private void refreshSessionIfStale() throws SessionException {
refresh(false);
}
}
/**
* The time (in milliseconds from the UTC epoch) until this session can be removed from a session blacklist. This
* is guaranteed to be some time after the session has expired.
*
* @return the at which the session expires (if it has not already) plus a purge delay.
* @throws BlacklistException if the session has already expired or an error occurs.
*/
public long getBlacklistExpiryTime() throws BlacklistException {
try {
return sessionExpiryTime;
} catch (SessionException e) {
throw new BlacklistException(e);
}
}
/**
* Returns the state of the session.
*
* @param reset
* This parameter indicates that whether the Session Service
* needs to reset the latest access time on this session.
* @return The state of the session. The session state is one of the
* following: <code>INVALID, VALID, INACTIVE, and DESTROYED</code>.
* @exception SessionException is thrown if the session reached its
* maximum session time, or the session was destroyed, or
* there was an error during communication with session
* service.
*/
} else {
if (reset) {
needToReset = true;
}
}
return sessionState;
}
}
/**
* Returns the type of the event which caused the state change of this
* session.
*
* @return The type of the event. The event types are defined in class
* SessionEvent as static integers : SESSION_CREATION, IDLE_TIMEOUT,
* MAX_TIMEOUT, LOGOUT, ACTIVATION, REACTIVATION, and DESTROY.
*/
public int getEventType() {
return eventType;
}
/**
* Gets the property stored in this session.
*
* @param name The property name.
* @return The property value in String format.
* @exception SessionException is thrown if the session reached its
* maximum session time, or the session was destroyed, or
* there was an error during communication with session
* service.
*/
if (name == null ? sessionCookies.getLBCookieName() != null : !name.equals(sessionCookies.getLBCookieName())) {
refresh(false);
}
}
}
/**
* 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 {
try {
}
catch (Exception e) {
throw new SessionException(e);
}
return masterSID;
}
/**
* Returns true if the SSOTokenID associated with this SSOToken is a
* restricted token, false otherwise.
*
* @return true if the token is restricted
* @throws SSOException If we are unable to determine if the session is
* restricted
*/
public boolean isRestricted() throws SessionException {
return (this.getRestriction() != null ? true : false);
}
/**
* Gets the property stored in this session.
*
* @param name The property name.
* @return The property value in String format only
* when run in the server mode else return null
*/
if (SystemProperties.isServerMode()) {
}
return null;
}
/**
* Sets a property for this session.
*
* @param name The property name.
* @param value The property value.
* @exception SessionException if the session reached its maximum session
* time, or the session was destroyed, or there was an error
* during communication with session service, or if the property
* name or value was null.
*/
throw new SessionException("Session property name/value cannot be null");
}
try {
} catch (Exception e) {
throw new SessionException(e);
}
}
/**
* Used to find out if the maximum caching time has reached or not.
*/
public boolean maxCachingTimeReached() {
}
/**
* Gets the Session Service URL for this session object.
*
* @return The Session Service URL for this session.
* @exception SessionException when cannot get Session URL.
*/
if (SystemProperties.isServerMode()) {
}
// we can cache the result because in client mode
// session service location does not change
// dynamically
if (sessionServiceURL == null) {
}
return sessionServiceURL;
}
/**
* Destroys a session.
*
* @param session The session to be destroyed.
* @exception SessionException if there was an error during
* communication with session service, or the corresponding
* session was destroyed.
*/
try {
} catch (Exception e) {
throw new SessionException(e);
}
finally {
}
}
/**
* Logs out a session.
*
* @throws SessionException if there was an error during communication
* with session service. If the session logged out already,
* no exception will be thrown.
*/
public void logout() throws SessionException {
try {
} catch (Exception e) {
throw new SessionException(e);
}
}
/**
* Adds a session listener for session change events.
*
* @param listener Session Listener object.
* @exception SessionException if the session state is not valid.
*/
addSessionListener(listener, false);
}
/**
* Adds a session listener for session change events.
*
* @param listener Session Listener object.
* @param force whether to ignore whether a Session is in the Invalid state. If false will throw an exception if
* the Session is Invalid.
* @exception SessionException if the session state is not valid.
*/
}
}
public void scheduleToTimerPool() {
if (sessionPollerPool.isPollingEnabled()) {
if (sessionPollerPool.getCacheBasedPolling()) {
}
if (scheduledExecutionTime() > timeoutTime) {
cancel();
}
if (!isScheduled()) {
}
} else {
if (scheduledExecutionTime() > timeoutTime) {
cancel();
}
if (!isScheduled()) {
}
}
}
}
/**
* Returns true if the provided time is less than Long.MAX_VALUE seconds.
*/
private boolean willExpire(long minutes) {
}
/**
* Gets all valid sessions from the specified session server. This session
* is subject to access control in order to get all sessions.
*
* @param server
* The session server name. If the server name contains protocol
* and port, the protocol and port will be used. Otherwise, the
* server protocol and port is default to the same protocol and
* port of the calling session.
* @return A Vector of Session objects.
* @exception SessionException if there was an error during
* communication with session service.
*/
throws SessionException {
if (pos != -1) {
}
if (pos != -1) {
if (pos1 != -1 ) {
}
}
}
/**
* Get all the event listeners for this Session.
*
* @return SessionEventListener vector
*/
return localSessionEventListeners;
}
/**
* Returns all the valid sessions for a particular Session Service URL. If a
* user is not allowed to access the Sessions of the input Session Server,
* it will return null.
*
* @param svcurl Session Service URL.
* @exception SessionException
*/
throws SessionException {
try {
int status;
int totalResultCount;
boolean isLocal = false;
isLocal = true;
} else {
}
}
}
}
}
/**
* Gets the latest session from session server and updates the local cache
* of this session.
*
* @param reset The flag to indicate whether to reset the latest session
* access time in the session server.
* @exception SessionException if the session reached its
* maximum session time, or the session was destroyed, or
* there was an error during communication with session
* service.
*/
// recalculate whether session is local or remote on every refresh
// this is just an optmization
// it is functionally safe to always use remote mode
// but it is not efficient
// this check takes care of migration "remote -> local"
// reverse migration "local - > remote" will be
// done by calling Session.markNonLocal() from
// SessionService.handleReleaseSession()
if (activeContext == null) {
activeContext = this.context;
}
try {
new RestrictedTokenAction() {
return null;
}
});
} catch (Exception e) {
if (sessionDebug.messageEnabled()) {
+ sessionID);
}
throw new SessionException(e);
}
}
/*
* Refreshes the Session Information
* @param <code>true</code> refreshes the Session Information
*/
needToReset = false;
long oldMaxCachingTime = maxCachingTime;
long oldMaxIdleTime = maxIdleTime;
long oldMaxSessionTime = maxSessionTime;
}
}
/**
* Updates the session from the session information server.
*
* @param info Session Information.
*/
}
}
}
}
}
if (timedOutAt <= 0) {
if (sessionTimedOutProp != null) {
try {
} catch (NumberFormatException e) {
+ sessionTimedOutProp, e);
}
}
}
// note : do not use getProperty() call here to avoid unexpected
// recursion via
// refresh()
if (restrictionProp != null) {
try {
} catch (Exception e) {
throw new SessionException(e);
}
}
}
/**
* Sets a token restriction on this session. Optional operation - specific sub-classes can throw an exception if
* not supported.
*
* @param restriction the restriction to apply to this session.
* @throws UnsupportedOperationException if this session type does not support token restrictions.
*/
this.restriction = restriction;
}
/**
* populate context object with admin token
* @exception SessionException
* @param appSSOToken application SSO Token to bet set
*/
{
if (appSSOToken == null) {
if (sessionDebug.warningEnabled()) {
+ "createContext():, "
+ "cannot obtain application SSO token, "
+ "defaulting to IP address");
}
} else {
+ "createContext():, "
+ "setting context to application SSO token");
}
}
/**
* Handle exception coming back from server in the Sessionresponse
* @exception SessionException
* @param sres SessionResponse object holding the exception
*/
void processSessionResponseException(SessionResponse sres, SSOToken appSSOToken) throws SessionException {
try {
if (sessionDebug.messageEnabled()) {
+ "processSessionResponseException: exception received"
}
// Check if this exception was thrown due to Session Time out or not
// If yes then set the private variable timedOutAt to the current
// time But before that check if this timedOutAt is already set
// or not. No need of setting it again
if(timedOutAt <= 0) {
}
}
"appTokenInvalid")) != -1) {
if (sessionDebug.messageEnabled()) {
+ "processSessionResponseException: AppTokenInvalid = TRUE");
}
if (!SystemProperties.isServerMode()) {
if (sessionDebug.messageEnabled()) {
+ "processSessionResponseException: Destroying AppToken");
}
if (sessionDebug.warningEnabled()) {
+"processSessionResponseException"
+" processSessionResponseException"
+": server responded with app token invalid"
+" error,refetching the app sso token");
}
if (sessionDebug.messageEnabled()) {
+ "processSessionResponseException: creating New AppToken"
+ " TokenID = " + newAppSSOToken);
}
} else {
if (sessionDebug.messageEnabled()) {
+ "processSessionResponseException: AppToken invalid in" +
" server mode; throwing exception");
}
}
} else {
}
throw new SessionException(ex);
}
}
/**
* Add listener to Internal Session.
*/
public void addInternalSessionListener() {
try {
}
if (isLocal()) {
} else {
}
} catch (Exception e) {
//todo : something! :-D
}
}
/**
* Returns true if cookies are supported else false. The
* <code>cookieSupport</code> value is first determined from the Session ID
* object , if that is null then it is determined based on the cookie mode
* value set in the Session object else <code>cookieSupport</code> value is
* retrieved from the session property <code>cookieSupport</code>. If
* cookie Support value is not determined then the the default "false" is
* assumed.
*/
public boolean getCookieSupport() {
boolean cookieSupport = false;
try {
if (cookieMode != null) {
} else if (this.cookieMode != null) {
} else {
if (cookieSupportStr != null) {
}
}
cookieSupport = true;
}
if (sessionDebug.messageEnabled()) {
}
return cookieSupport;
}
/**
* Set the cookie Mode based on whether the request has cookies or not. This
* method is called from <code>createSSOToken(request)</code> method in
* <code>SSOTokenManager</code>.
*
* @param cookieMode whether request has cookies or not.
*/
if (sessionDebug.messageEnabled()) {
}
if (cookieMode != null) {
this.cookieMode = cookieMode;
}
}
/**
* Indicates whether local or remote invocation of Sesion Service should be
* used
*
* @return true if local invocation should be used, false otherwise
*/
boolean isLocal() {
return sessionIsLocal;
}
/**
* Actively checks whether current session should be considered local (so
* that local invocations of Session Service methods are to be used).
*
* @return true if the session local.
*/
private boolean checkSessionLocal() throws SessionException {
if (SystemProperties.isServerMode()) {
} else {
return false;
}
}
return restriction;
}
Object getContext() {
return context;
}
/**
* Returns a stable ID that can be used as a unique identifier when storing this session.
*
* @return a unique stable storage id.
*/
public String getStableStorageID() {
}
}