CTSPersistentSAML2Store.java revision 9f70e00b4c0aea24675f90732e58a16d17d67635
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Copyright (c) 2012 ForgeRock US Inc. All Rights Reserved
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * The contents of this file are subject to the terms of the Common Development and
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Distribution License (the License). You may not use this file except in compliance with the
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * specific language governing permission and limitations under the License.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * When distributing Covered Software, include this CDDL Header Notice in each file and include
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Header, with the fields enclosed by brackets [] replaced by your own identifying
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * information:
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * "Portions copyright [year] [name of copyright owner]".
c793af95640863cd29868fc7c419c5d2496b207bsangeetaimport com.iplanet.dpro.session.exceptions.StoreException;
c793af95640863cd29868fc7c419c5d2496b207bsangeetaimport com.iplanet.dpro.session.service.SessionService;
c793af95640863cd29868fc7c419c5d2496b207bsangeetaimport com.iplanet.dpro.session.share.SessionBundle;
c793af95640863cd29868fc7c419c5d2496b207bsangeetaimport com.sun.identity.coretoken.interfaces.AMTokenSAML2Repository;
c793af95640863cd29868fc7c419c5d2496b207bsangeetaimport com.sun.identity.shared.configuration.SystemPropertiesManager;
c793af95640863cd29868fc7c419c5d2496b207bsangeetaimport org.forgerock.openam.session.model.AMRootEntity;
edd26dc5eeb3b093945c371e4b6dd8286348d53fdr * This class is used in SAML2 mode to store/recover serialized
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * state of Assertion/Response object.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * This class acts as a proxy to perform distinct SAML2
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * operations and allow the CTSPersistentStore implementation
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * to handle the actual CRUD for Tokens.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmarkpublic class CTSPersistentSAML2Store extends GeneralTaskRunnable
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * This Instances Server ID.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Indicator for Persistence Store.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static boolean isDatabaseUp = false;
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static boolean lastLoggedDBStatusIsUp = false;
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Provides a Reference the AMTokenSAML2Repository Implementation Class for our
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * persistence operations.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * This instance is this classes Singleton.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static volatile AMTokenSAML2Repository instance = null;
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * OpenAM CTS Repository.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * This instance actually brings in the implementation for all CRUD
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Operations.
44b099c4d944a196d124a02c7403ad891223139eSowmini Varadhan private static volatile AMTokenSAML2Repository amTokenSAML2Repository = null;
44b099c4d944a196d124a02c7403ad891223139eSowmini Varadhan * grace period before expired session records are removed from the
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * repository
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark private static long gracePeriod = 5 * 60; /* 5 mins in secs */
c793af95640863cd29868fc7c419c5d2496b207bsangeeta "com.sun.identity.session.repository.cleanupGracePeriod";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta "SAML2 failover service is not functional due to Token Persistent Store unavailability.";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta "SAML2 Token Persistent Store is not available at this moment."
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark + "Please check with the system administrator " +
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark "for appropriate actions";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static final String LOG_MSG_DB_BACK_ONLINE =
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark "SESSION_DATABASE_BACK_ONLINE";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static final String LOG_MSG_DB_UNAVAILABLE =
c793af95640863cd29868fc7c419c5d2496b207bsangeeta "SESSION_DATABASE_UNAVAILABLE";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Time period between two successive runs of repository cleanup thread
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * which checks and removes expired records
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static long cleanUpPeriod = 5 * 60 * 1000; // 5 min in milliseconds
188e166434dcdde5356d87fb06c169f15dc4dca9Erik Nordmark public static final String CLEANUP_RUN_PERIOD =
c793af95640863cd29868fc7c419c5d2496b207bsangeeta "com.sun.identity.saml2.repository.cleanupRunPeriod";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Time period between two successive runs of DBHealthChecker thread which
188e166434dcdde5356d87fb06c169f15dc4dca9Erik Nordmark * checks for Database availability.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static long healthCheckPeriod = 1 * 60 * 1000;
c793af95640863cd29868fc7c419c5d2496b207bsangeeta public static final String HEALTH_CHECK_RUN_PERIOD =
188e166434dcdde5356d87fb06c169f15dc4dca9Erik Nordmark "com.sun.identity.saml2.repository.healthCheckRunPeriod";
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * This period is actual one that is used by the thread. The value is set to
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * the smallest value of cleanUPPeriod and healthCheckPeriod.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta private static long runPeriod = 1 * 60 * 1000; // 1 min in milliseconds
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Static Initialization Stanza.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta gracePeriod = Integer.parseInt(SystemPropertiesManager.get(
c793af95640863cd29868fc7c419c5d2496b207bsangeeta CLEANUP_GRACE_PERIOD, String.valueOf(gracePeriod)));
c793af95640863cd29868fc7c419c5d2496b207bsangeeta debug.error("Invalid value for " + CLEANUP_GRACE_PERIOD
c793af95640863cd29868fc7c419c5d2496b207bsangeeta + ", using default");
c793af95640863cd29868fc7c419c5d2496b207bsangeeta cleanUpPeriod = Integer.parseInt(SystemPropertiesManager.get(
c793af95640863cd29868fc7c419c5d2496b207bsangeeta CLEANUP_RUN_PERIOD, String.valueOf(cleanUpPeriod)));
c793af95640863cd29868fc7c419c5d2496b207bsangeeta debug.error("Invalid value for " + CLEANUP_RUN_PERIOD
c793af95640863cd29868fc7c419c5d2496b207bsangeeta + ", using default");
c793af95640863cd29868fc7c419c5d2496b207bsangeeta .parseInt(SystemPropertiesManager.get(HEALTH_CHECK_RUN_PERIOD,
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.error("Invalid value for " + HEALTH_CHECK_RUN_PERIOD
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark + ", using default");
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark runPeriod = (cleanUpPeriod <= healthCheckPeriod) ? cleanUpPeriod
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Instantiate the Singleton Instance.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.error("Unable to Instantiate "+CTSPersistentSAML2Store.class.getName()+" for SAML2 Persistence",e);
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Constructs new CTSPersistentSAML2Store
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng * @exception Exception when cannot create a new SAML2 repository
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark private CTSPersistentSAML2Store() throws Exception {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Obtain OpenAM Instance Configuration Properties.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark String thisSessionServerProtocol = SystemPropertiesManager
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng String thisSessionServer = SystemPropertiesManager
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng String thisSessionServerPortAsString = SystemPropertiesManager
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark String thisSessionURI = SystemPropertiesManager
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng .get(Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark throw new SessionException(SessionBundle.rbName,
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Obtain our Server ID for this OpenAM Instance.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark serverId = WebtopNaming.getServerID(thisSessionServerProtocol,
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark thisSessionServer, thisSessionServerPortAsString,
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Initialize our Persistence Layer.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta // Schedule our Runnable Background Thread Task. @see run() method for associated Task.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Initialize the Reference to our CTS Persistent Store.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark private void initPersistSession() {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Obtain our AM Token Repository Instance to provide the Backend CRUD for Tokens.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark amTokenSAML2Repository = CTSPersistentSAML2StoreFactory.getInstance();
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // This Throw will be caught below.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark throw new Exception("Unable to acquire AMTokenSAML2Repository Implementation Reference from Factory!");
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng * Provide Service Instance Access to our Singleton
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng * @return AMTokenSAML2Repository Singleton Instance.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark public static AMTokenSAML2Repository getInstance() {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * Retrives existing SAML2 object from persistent Repository.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @param samlKey primary key
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng * @return Object - SAML2 unMarshaled Object, if failed, return null.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark public Object retrieveSAML2Token(String samlKey) {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Retrieve the SAML2 Token from the Repository using the SAML2 Primary Key.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark AMRecord amRecord = (AMRecord) amTokenSAML2Repository.retrieveSAML2Token(samlKey);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Obtain the Serialized Internal Session to unMarshal the SAML2 Object.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark byte[] blob = amRecord.getSerializedInternalSessionBlob();
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark Object unMarshaledObject = SessionUtils.decode(blob);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.message("AMTokenSAML2Repository.retrieveSAML2Token(): failed retrieving "
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark + "SAML2 object", e);
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng * Retrives a list of existing SAML2 object from persistent datastore with
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * secodaryKey
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @param secKey Secondary Key
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @return SAML2 object, if failed, return null.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark public List<Object> retrieveSAML2TokenWithSecondaryKey(String secKey) {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Retrieve the SAML2 Token from the Repository using the SAML2 Primary Key.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark List<Object> results = amTokenSAML2Repository.retrieveSAML2TokenWithSecondaryKey(secKey);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark if ( (results == null) || (results.size() <= 0) )
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark FAMRecord famRecord = (FAMRecord) results.get(0);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Obtain the Additional Attributes and UnMarshal.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark Map map = famRecord.getExtraStringAttributes();
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark Vector blobs = (Vector)map.values().iterator().next();
e11c3f44f531fdff80941ce57c065d2ae861cefcmeem } catch (Exception e) {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.message("AMTokenSAML2Repository.retrieveSAML2TokenWithSecondaryKey(): failed retrieving "
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark + "SAML2 object", e);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * Deletes the SAML2 object by given primary key from the repository
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @param samlKey primary key
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark public void deleteSAML2Token(String samlKey) {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark amTokenSAML2Repository.deleteSAML2Token(samlKey);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.error("AMTokenSAML2Repository.deleteSAML2Token(): failed deleting "
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark + "SAML2 object", e);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * Deletes expired SAML2 object from the repository
9e3469d3db608feb0e43d9955cbf406c22025463Erik Nordmark * @exception Exception When Unable to delete the expired SAML2 objects
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark amTokenSAML2Repository.deleteExpiredSAML2Tokens();
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.error("AMTokenSAML2Repository.deleteExpiredSAML2Tokens(): failed "
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark + "deleting Expired saml2 object", e);
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * Saves SAML2 data into the SAML2 Repository
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @param samlKey primary key
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @param samlObj saml object such as Response, IDPSession
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark * @param expirationTime expiration time
da14cebe459d3275048785f25bd869cb09b5307fEric Cheng * @param secKey Secondary Key
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark public void saveSAML2Token(String samlKey, Object samlObj, long expirationTime,
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Is our Token Repository Available?
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Save the SAML2 Token.
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark // Perform the Save of the Token to the Token Repository.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta amTokenSAML2Repository.saveSAML2Token(samlKey, samlObj, expirationTime, secKey);
c793af95640863cd29868fc7c419c5d2496b207bsangeeta debug.error("AMTokenSAML2Repository.saveSAML2Token(): failed "
c793af95640863cd29868fc7c419c5d2496b207bsangeeta + "to save SAML2 object", e);
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * This method is invoked to log a message in the following two cases:
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * (1) the DB is detected down by either the user requests
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * (retrieve/save/delete) or the background checker thread:
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Log message: HA_DATABASE_UNAVAILABLE (2) the DB is detected
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * available again by the background health checker thread => Log message:
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * HA_DATABASE_BACK_ONLINE
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * The flag "lastLoggedDBStatusIsUp" is used to avoid logging the same DB
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * status again and again if the status actually doesn't change over time.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Please also note that if the DB is already down in the very beginning
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * when starting the AM instance, there will be no message being logged
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * since at this time the session service is not fully initialized yet
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * therefore no sso token can be generated and used for the logging purpose.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Nevertheless, the appropriate logging will be done later when the
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * background thread kicks in.
188e166434dcdde5356d87fb06c169f15dc4dca9Erik Nordmark private void logDBStatus() {
c793af95640863cd29868fc7c419c5d2496b207bsangeeta SessionService ss = SessionService.getSessionService();
29bc4795a563df76952f94da2aa3b7daa8abf972sangeeta * Return Service Run Period.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * @return long current run period.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta public long getRunPeriod() {
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * Service Method.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * @param obj
c793af95640863cd29868fc7c419c5d2496b207bsangeeta return false;
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * Service Method.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * @param obj
c793af95640863cd29868fc7c419c5d2496b207bsangeeta return false;
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Service Method.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta public boolean isEmpty() {
c793af95640863cd29868fc7c419c5d2496b207bsangeeta return true;
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * Monitoring logic used by background thread This thread is used for both
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * cleanup of expired sessions in the repository and for the Repository health
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * checking. The thread always runs with smallest value of cleanUpPeriod and
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * healthCheckPeriod.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta public void run() {
c793af95640863cd29868fc7c419c5d2496b207bsangeeta String classMethod="CTSPersistentSAML2Store.run: ";
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh debug.message(classMethod + "Cleaning expired SAML2 records");
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * Clean up is done based on the cleanUpPeriod even though the
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * thread runs based on the runPeriod.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta if (SAML2Utils.isSAML2FailOverEnabled() && (cleanUpValue <= 0)) {
f4b3ec61df05330d25f55a36b975b4d7519fdeb1dh * HealthChecking is done based on the runPeriod but only when
c793af95640863cd29868fc7c419c5d2496b207bsangeeta * the Database is down.
c793af95640863cd29868fc7c419c5d2496b207bsangeeta if (SAML2Utils.isSAML2FailOverEnabled() && (!isDatabaseUp)) {
c793af95640863cd29868fc7c419c5d2496b207bsangeeta } catch (Exception e) {
bd670b35a010421b6e1a5536c34453a827007c81Erik Nordmark debug.error("CTSPersistentSAML2Store.run(): Exception in thread",