SessionID.java revision d0da70ccbba38b773e7a7cc71bc124b06206d201
/**
* 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: SessionID.java,v 1.10 2009/10/02 23:45:42 qcheng Exp $
*
*/
/**
<<<<<<< HEAD
* Portions Copyrighted 2011-2015 ForgeRock AS.
=======
* Portions Copyrighted 2011-2015 ForgeRock, AS.
>>>>>>> stateless
*/
/**
* The <code>SessionID</code> class is used to identify a Session object. It
* contains a random String and the name of the session server. The random
* String in the Session ID is unique on a given session server.
*
* @see com.iplanet.dpro.session.Session
*/
public class SessionID implements Serializable {
private boolean isParsed = false;
private boolean comingFromAuth = false;
static {
if (cookieName == null) {
}
}
// prefix "S" is reserved to be used by session framework-specific
// extensions for session id format
protected SecureRandom initialValue() {
try {
try {
} catch (NoSuchProviderException e) {
}
} catch (Exception e) {
throw new IllegalStateException("Need SHA1PRNG algorithm to continue");
}
}
};
/**
* Constructs a <code>SessionID</code> object based on a
* <code>HttpServletRequest</code> object. but if cookie is not found it
* checks the URL for session ID.
*
* @param request <code>HttpServletRequest</code> object which contains
* the encrypted session string.
*/
if (cookieName == null) {
}
if (cookieName != null) {
// check if this is a forward from authentication service case.
// if yes, find Session ID in the request URL first, otherwise
// find Session ID in the cookie first
if (debug.messageEnabled()) {
+ isForward);
}
if (realReqSid != null) {
} else {
if (cookieValue != null) {
}
}
} else {
// if no cookie found in the request then check if
// the URL has it.
if (cookieValue == null) {
if (realReqSid != null) {
}
} else {
}
}
}
}
/**
* Creates a default instance of SessionID with a null Session ID.
* Note: This function is needed for deserialisation.
*/
public SessionID() {
}
/**
* Constructs a <code>SessionID</code> object based on a Session ID.
*
* @param sid The session ID String in an encrypted format.
*/
// toString() returns a String that is identical to 'sid'
}
/**
* Checks if encrypted string is null or empty
*
* @return true if encrypted string is null or empty.
*/
public boolean isNull() {
return isNull(encryptedString);
}
/**
* Utility method to check if argument is null or empty string
*
* @param s string to check
* @return true if <code>s</code> is null or empty.
*/
}
/**
* Returns the session server URI in this object.
*
* @return The session server URI in this object.
*/
public String getSessionServerURI() {
if (isNull(sessionServerURI)) {
}
return sessionServerURI;
}
/**
* This method returns the boolean representing if this session id
* is a regular auth token, generated via AuthContext API
* and not a restricted one.
*
* @return The boolean representing if this session id
* is that of a regular auth token, generated via AuthContext API
*/
public boolean getComingFromAuth() {
if (debug.messageEnabled()) {
+"comingFromAuth:"+comingFromAuth);
}
return comingFromAuth;
}
/**
* This method sets the boolean representing if this session id
* is a regular auth token, generated via AuthContext API
* @param comingFromAuth boolean representing if the
* token has been generated by AuthContext and is a regular token,
* not restricted one.
*/
public void setComingFromAuth(boolean comingFromAuth) {
this.comingFromAuth = comingFromAuth;
}
/**
* Returns the session server name in this object.
*
* @return The session server protocol in this object.
*/
public String getSessionServerProtocol() {
if (isNull(sessionServerProtocol)) {
}
return sessionServerProtocol;
}
/**
* Gets the session server port in this object
*
* @return The session server port in this object.
*/
public String getSessionServerPort() {
if (isNull(sessionServerPort)) {
}
return sessionServerPort;
}
/**
* Gets the session server name in this object.
*
* @return The session server name in this object.
*/
public String getSessionServer() {
if (isNull(sessionServer)) {
}
return sessionServer;
}
/**
* Gets the domain where this session belongs to.
*
* @return The session domain name.
*/
public String getSessionDomain() {
return sessionDomain;
}
/**
* Gets the session server id in this object.
*
* @return The session server id in this object.
*/
public String getSessionServerID() {
if (isNull(sessionServerID)) {
}
return sessionServerID;
}
/**
* Returns the encrypted session string. By doing so it also makes it possible to use this string representation
* for serializing/deserializing SessionID objects for session failover.
*
* @return An encrypted session string.
* @see org.forgerock.openam.cts.utils.JSONSerialisation
*/
return encryptedString;
}
/**
* Compares this Session ID to the specified object. The result is true if
* and only if the argument is not null and the random string and server
* name are the same in both objects.
*
* @param object the object to compare this Session ID against.
* @return true if the Session ID are equal; false otherwise.
*/
return false;
}
}
/**
* Returns a hash code for this object.
*
* @return a hash code value for this object.
*/
public int hashCode() {
// Since SessionID is immutable, it's hashCode doesn't change.
return encryptedString.hashCode();
}
/**
* Extracts the server, protocol, port, extensions and tail from Session ID
*
*/
private void parseSessionString() {
// parse only once
if (isParsed) {
return;
}
/**
* This check is done because the SessionID object is getting created
* with empty sid value. This is a temparory fix. The correct fix for
* this is, throw a SessionException while creating the SessionID
* object.
*/
if (isNull()) {
throw new IllegalArgumentException("sid value is null or empty");
}
try {
// sidString would have * if it has been c66 encoded
}
if (outerIndex == -1) {
isParsed = true;
return;
}
if (tailIndex != -1) {
}
}
} catch (Exception e) {
}
isParsed = true;
}
/**
* Optimised alternative to DataInputStream#readUTF (which is very slow).
*
* @param extensionPart the extension map part of the session id
* @return the parsed extension map
* @throws IOException if there is an error decoding the extensions
*/
// The bytes are actually written via DataOutputStream#writeUTF which uses "modified UTF-8". We decode this
// as normal UTF-8. This should only cause an issue with 'null' characters (\u0000), which should never
// appear anyway.
throw new IllegalArgumentException("Invalid Base64-encoded data");
}
// Data is encoded as a 2-byte unsigned short length, followed by 'length' bytes of UTF-8
i += 2;
i += length;
i += 2;
i += length;
}
return extMap;
}
/**
* Parses the next two bytes from the given byte array as an unsigned short value. As Java does not support
* unsigned types, we return the value as the least-significant bits of a signed integer. The most-significant
* 16 bits of the result will always be 0. Assumes Big-Endian format as in DataInput#readUnsignedShort.
*
* @param bytes the byte array to read the unsigned short from.
* @param i the offset into the byte array of the start of the unsigned short.
* @return the unsigned short as a (positive) signed integer.
*/
private static int parseUnsignedShort(final byte[] bytes, final int i) {
}
/**
* Sets the server info by making a naming request by passing
* its id which is in session id and parses it.
* @param id ServerID
*/
try {
while (idx > 0) {
}
} catch (Exception e) {
throw new IllegalArgumentException(
}
}
/**
* Returns tail part of session id
*
* @return An opaque tail part of session id
*/
return tail;
}
/**
* Returns the if the cookies are supported.
*
* @return Boolean object value which is Boolean.<code>TRUE<code> if
* supported <code>FALSE</code> otherwise
*/
public Boolean getCookieMode() {
return cookieMode;
}
/**
* Retrieves extension value by name Currently used session id extensions
* are
*
* <code>SessionService.SITE_ID</code> server id (from platform server list)
* hosting this session (in failover mode this will be server id of the
* load balancer)
*
* <code>SessionService.PRIMARY_ID</code>,
* <code>SessionService.SECONDARY_ID</code> used if internal request
* routing mode is enabled.
*
* @param name Name of the session ID extension.
* @return extension.
*/
}
/**
* Generates properly encoded session id string given the encrypted ID,
* extension map and tail part
*
* @param encryptedID encrypted part of session ID.
* @param extensions map of session ID extensions.
* @param tail tail part of session ID (currently used to carry associated
* HTTP session ID)
* @return encoded session id string.
* @throws SessionException
*/
try {
}
if (extensions != null) {
.hasNext();) {
}
}
}
if (c66EncodeCookie()) {
}
return returnValue;
} catch (Exception e) {
throw new SessionException(e);
}
}
/**
* Generates encoded session id string which uses the same extensions and
* tail part as prototype session id, but a different encrypted ID. This
* method is used to generate session handle and restricted token id for a
* given master session id. Related session IDs must share extensions and
* tail information in order for session failover to work properly
*
* @param encryptedID encrypted ID.
* @param prototype session ID to copy extensions and tail from
* @return encoded session id
* @throws SessionException
*/
}
/**
* Checks whether session id needs to be c66 encoded to convert to cookie
* value.
* @return <code>true</code> if session id needs to be c66 encoded to
* convert to cookie value. Otherwise returns <code>false</code>.
* c66 encoding is opensso specific url safe char66 encoding.
*
* @see #c66EncodeSidString(String)
* @see #c66DecodeCookieString(String)
*/
private static boolean c66EncodeCookie() {
"false")).booleanValue();
}
/**
* Converts native session id string to opensso specific url safe char66
* encoded string.
* This is not a general purpose utility.
* This is meant only for internal use
*
* @param sidString plain text string
* @return url safe modifed char66 encoded string
*
* @see #c66DecodeCookieString(String)
*
* Sample session id string:
* AQIC5wM2LY4SfcxPEcjVKCEI7QdmYvlOZvKZpdEErxVPvx8=@AAJTSQACMDE=#
*
* We would replace
* + with -
* / with _
* = with .
* @ with star
* # with star
*
* while reconstucting the original cookie value first occurence of
* star would be replaced with @ and the subsequent occurunce star would
* be replaced with #
*/
return sidString;
}
for (int i = 0; i < length; i++) {
if (c == '+') {
chars[i] = '-';
} else if (c == '/') {
chars[i] = '_';
} else if (c == '=') {
chars[i] = '.';
} else if (c == '@') {
chars[i] = '*';
} else if (c == '#') {
chars[i] = '*';
} else {
chars[i] = c;
}
}
}
/**
* Converts opensso specific url safe char66
* encoded string to native session id string.
* This is not a general purpose utility.
* This is meant only for internal use
*
* @param urlEncodedString opensso specific url safe char66 encoded string
* @return native session id string
*
* @see #c66EncodeSidString(String)
*
* We would replace
* - with +
* _ with /
* . with =
* first occurence of star with @
* subsequent occurence of star with #
*/
return urlEncodedString;
}
boolean firstStar = true;
for (int i = 0; i < length; i++) {
char c = urlEncodedString.charAt(i);
if (c == '-') {
chars[i] = '+';
} else if (c == '_') {
chars[i] = '/';
} else if (c == '.') {
chars[i] = '=';
} else if (c == '*') {
if (firstStar) {
firstStar = false;
chars[i] = '@';
} else {
chars[i] = '#';
}
} else {
chars[i] = c;
}
}
}
public SessionID generateRelatedSessionID(SessionServerConfig serverConfig) throws SessionException {
}
/**
* @return true if this SessionID actually represents a session handle.
*/
public boolean isSessionHandle() {
}
return SHANDLE_SCHEME_PREFIX + SessionID.makeRelatedSessionID(generateEncryptedID(serverConfig), this);
}
return Long.toHexString(
}
/**
* Generates new encrypted ID string to be used as part of session id
*
* @return emcrypted ID string
*/
// 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 serverConfig Required server configuration
* @param domain session domain
* @param jwt The JWT to encode as part of Stateless Sessions.
*
* @return newly generated session id
* @throws SessionException
*/
public static SessionID generateSessionID(SessionServerConfig serverConfig, String domain, String jwt) throws SessionException {
// AME-129 Required for Automatic Session Failover Persistence
if (serverConfig.isSiteEnabled() &&
} else {
}
}
// AME-129, always set a Storage Key regardless of persisting or not.
ext.put(SessionID.STORAGE_KEY, String.valueOf(secureRandom.getInstanceForCurrentThread().nextLong()));
return sid;
}
this(sid);
}
/**
* This method validates that the received session ID points to an existing server ID, and the site ID also
* corresponds to the server ID found in the session. Within this method two "extensions" are of interest: SITE_ID
* and PRIMARY_ID. The PRIMARY_ID extension contains the hosting server's ID, but only if the given server belongs
* to a site. The SITE_ID extension contains either the primary site's ID (if the hosting server belongs to a site)
* or the hosting server's ID. This method will look at the extensions and make sure that they match up with the
* naming table of this environment. If there is a problem with the session ID (e.g. the server ID actually points
* to a primary or secondary site, or if the server ID doesn't actually correlate with the site ID), then a
* SessionException is thrown in order to prevent forwarding of the received session request. A possible scenario
* for having such an incorrect session ID would be having multiple OpenAM environments using the same cookie
* domain and cookie name settings.
*
* @throws SessionException If the validation failed, possibly because the provided session ID was malformed or not
* created within this OpenAM deployment.
*/
public void validate() throws SessionException {
//In this case by definition the server is not assigned to a site, so we want to ensure that the
//SITE_ID points to a server
errorMessage = "Invalid session ID, Site ID \"" + siteID + "\" either points to a non-existent server,"
+ " or to a site";
}
+ "corresponding site ID is not present in the session ID";
}
} else {
//PRIMARY_ID is not null, hence this session belongs to a site, we need to verify that the PRIMARY_ID
//and the SITE_ID are both correct, and they actually correspond to each other
errorMessage = "Invalid session ID, Primary ID \"" + primaryID + "\" either points to a non-existent "
+ "server, or to a site";
}
if (errorMessage == null) {
//The server from the session doesn't actually belong to a site
//The server from the session actually belongs to a different site
}
}
}
if (errorMessage != null) {
if (debug.warningEnabled()) {
}
throw new SessionException(errorMessage);
}
}
}