/* * 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 * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * 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: AuthContext.java,v 1.25 2009/11/21 01:12:59 qcheng Exp $ * * Portions Copyrighted 2010-2015 ForgeRock AS. */ package com.sun.identity.authentication; import com.iplanet.am.util.SystemProperties; import com.iplanet.dpro.session.SessionID; import com.iplanet.dpro.session.service.SessionService; import com.iplanet.services.comm.client.PLLClient; import com.iplanet.services.comm.share.Request; import com.iplanet.services.comm.share.RequestSet; import com.iplanet.services.comm.share.Response; import com.iplanet.services.naming.WebtopNaming; import com.iplanet.sso.SSOException; import com.iplanet.sso.SSOToken; import com.iplanet.sso.SSOTokenManager; import com.sun.identity.authentication.client.AuthClientUtils; import com.sun.identity.authentication.server.AuthContextLocal; import com.sun.identity.authentication.service.AMAuthErrorCode; import com.sun.identity.authentication.service.AuthException; import com.sun.identity.authentication.service.LoginState; import com.sun.identity.authentication.share.AuthXMLTags; import com.sun.identity.authentication.share.AuthXMLUtils; import com.sun.identity.authentication.spi.AuthLoginException; import com.sun.identity.authentication.util.ISAuthConstants; import com.sun.identity.security.AMSecurityPropertiesException; import com.sun.identity.security.AdminTokenAction; import com.sun.identity.shared.Constants; import com.sun.identity.shared.debug.Debug; import com.sun.identity.shared.locale.L10NMessageImpl; import com.sun.identity.shared.xml.XMLUtils; import org.forgerock.guice.core.InjectorHolder; import org.forgerock.openam.authentication.service.protocol.RemoteHttpServletRequest; import org.forgerock.openam.authentication.service.protocol.RemoteHttpServletResponse; import org.forgerock.openam.session.SessionCache; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLStreamHandler; import java.security.AccessController; import java.security.KeyStore; import java.text.MessageFormat; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.Vector; /** * The AuthContext provides the implementation for * authenticating users. *

* A typical caller instantiates this class and starts the login process. * The caller then obtains an array of Callback objects, * which contains the information required by the authentication plug-in * module. The caller requests information from the user. On receiving * the information from the user, the caller submits the same to this class. * While more information is required, the above process continues until all * the information required by the plug-ins/authentication modules, has * been supplied. The caller then checks if the user has successfully * been authenticated. If successfully authenticated, the caller can * then get the Subject and SSOToken for the user; * if not successfully authenticated, the caller obtains the * AuthLoginException. *

* The implementation supports authenticating users either locally * i.e., in process with all authentication modules configured or remotely * to an authentication service/framework. (See documentation to configure * in either of the modes). * * @supported.api */ public class AuthContext extends Object implements java.io.Serializable { private java.util.Locale clientLocale = null; private String server_proto = SystemProperties.get(Constants.AM_SERVER_PROTOCOL); private String server_host = SystemProperties.get(Constants.AM_SERVER_HOST); private String server_port = SystemProperties.get(Constants.AM_SERVER_PORT); private String server_uri = SystemProperties.get(Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR); private boolean includeReqRes = SystemProperties.getAsBoolean(Constants.REMOTEAUTH_INCLUDE_REQRES); private static final String amAuthContext = "amAuthContext"; private static final String JSS_PASSWORD_UTIL = "com.sun.identity.authentication.util.JSSPasswordUtil"; private static final String JSSE_PASSWORD_CALLBACK = "com.sun.identity.security.keystore.AMCallbackHandler"; static String protHandlerPkg = System.getProperty(Constants.PROTOCOL_HANDLER, Constants.JSSE_HANDLER); static boolean usingJSSEHandler = protHandlerPkg.equals(Constants.JSSE_HANDLER); // Debug & I18N class protected static Debug authDebug = Debug.getInstance(amAuthContext); protected static ResourceBundle bundle = com.sun.identity.shared.locale.Locale.getInstallResourceBundle(amAuthContext); Status loginStatus = Status.IN_PROGRESS; String organizationName = ""; Document receivedDocument; AuthLoginException loginException = null; String hostName = null; private boolean forceAuth = false; private boolean localSessionChecked = false; String nickName = null; private URL authURL = null; private URL authServiceURL = null; private SSOToken ssoToken = null; private String ssoTokenID = null; private static SSOToken appSSOToken = null; com.sun.identity.authentication.server.AuthContextLocal acLocal = null; private final static int DEFAULT_RETRY_COUNT = 1; private int retryRunLogin = DEFAULT_RETRY_COUNT; /** * Variables for checking auth service is running local */ public boolean localFlag = false; /** * Variables for local AuthService identifier */ public static String localAuthServiceID; // Variable to check if 6.3 style remote AuthN has to be performed static boolean useOldStyleRemoteAuthentication; static boolean useNewStyleRemoteAuthentication; // this cookieTable is used to keep all the cookies retrieved from the // the PLL layer and replay them in subsequent auth requests, mainly for // persistence purpose. private HashMap cookieTable = new HashMap(); private HttpServletRequest remoteRequest = null; private HttpServletResponse remoteResponse = null; private final SessionCache sessionCache = SessionCache.getInstance(); /** * Constructs an instance of AuthContext for a given * organization name or sub organization name. This organization or * sub-organization name must be either "/" separated * ( where it starts with "/" ) , DN , Domain name or DNS Alias Name. *

* Caller would then use login to start the * authentication process and use getRequirements() and * submitRequirements() to pass the credentials * needed for authentication by the plugin authentication modules. * The method getStatus() returns the * authentication status. * * @param orgName Name of the user's organization. * @throws AuthLoginException if AuthContext creation fails. * This exception is kept for backward compatibility only. * * @supported.api */ public AuthContext(String orgName) throws AuthLoginException { organizationName = orgName; } /** * Constructs an instance of AuthContext for a given * organization name, or sub organization name and the OpenAM server * URL. * This organization or sub-organization name must be either "/" separated * ( where it starts with "/" ) , DN , Domain name or DNS Alias Name. * And the url should specify the OpenAM server's protocol, * host name, and port number, * for example : http://daye.red.iplanet.com:58080 * * Caller would then use login to start the * authentication process and use getRequirements() and * submitRequirements() to pass the credentials * needed for authentication by the plugin authentication modules. * The method getStatus() returns the * authentication status. * * @param orgName name of the user's organization * @param url URL of the OpenAm instance to talk to * @throws AuthLoginException if AuthContext creation fails. * This exception is kept for backward compatibility only. * * @supported.api */ public AuthContext(String orgName, URL url) throws AuthLoginException { organizationName = orgName; authURL = url; } /** * Constructs an instance of AuthContext for a given * organization name, or sub organization name and a nick name * for the certificate to be used in SSL handshake if client authentication * is turn on in the server side. * This organization or sub-organization name must be either "/" separated * ( where it starts with "/" ) , DN , Domain name or DNS Alias Name. * * This constructor would be mainly used for the Certificate based * authentication. If the certificate database contains multiple matching * certificates for SSL, this constructor must be called in order for the * desired certificate to be used for the Certificate based authentication. * * Caller would then use login to start the * authentication process and use getRequirements() and * submitRequirements() to pass the credentials * needed for authentication by the plugin authentication modules. * The method getStatus() returns the * authentication status. * * @param orgName name of the user's organization * @param nickName nick name for the certificate to be used * @throws AuthLoginException if AuthContext creation fails. * This exception is kept for backward compatibility only. * * @supported.api */ public AuthContext(String orgName, String nickName) throws AuthLoginException { organizationName = orgName; this.nickName = nickName; } /** * Constructs an instance of AuthContext for a given * organization name, or sub organization name, a nick name * for the certificate to be used in SSL handshake if client authentication * is enabled on the server side and the OpenAM URL. * This organization or sub-organization name must be either "/" separated * ( where it starts with "/" ) , DN , Domain name or a DNS Alias Name. * And the url should specify the OpenAM server's protocol, * host name, and port number, * for example : http://daye.red.iplanet.com:58080 * This constructor would be mainly used for the Certificate based * authentication. If the certificate database contains multiple matching * certificates for SSL, this constructor must be called in order for the * desired certificate to be used for the Certificate based authentication. * * Caller would then use login to start the * authentication process and use getRequirements() and * submitRequirements() to pass the credentials * needed for authentication by the plugin authentication modules. * The method getStatus() returns the * authentication status. * * @param orgName name of the user's organization * @param nickName nick name for the certificate to be used * @param url URL of the OpenAM server to talk to * @throws AuthLoginException if AuthContext creation fails. * This exception is kept for backward compatibility only. * * @supported.api */ public AuthContext(String orgName, String nickName, URL url) throws AuthLoginException { organizationName = orgName; this.nickName = nickName; authURL = url; } /** * Constructs an instance of AuthContext for a given * organization name, or sub organization name contained in the * single sign on token. * * This constructor should be called for re-authentication of an * authenticated user. single sign on token is the authenticated resource's * Single-Sign-On Token. If the session properties based on * the login method used matches those in the user's new * authenticated session then session upgrade will be done. * A new session containing properties from both old single sign on token * and new session shall be returned and old session will be * destroyed if authentication passes. * * Caller would then use login to start the * authentication process and use getRequirements() and * submitRequirements() to pass the credentials * needed for authentication by the plugin authentication modules. * The method getStatus() returns the * authentication status. * * @param ssoToken single sign on token representing the resource's previous * authenticated session. * @throws AuthLoginException if AuthContext creation fails. * This exception is kept for backward compatibility only. * * @supported.api */ public AuthContext(SSOToken ssoToken) throws AuthLoginException { this.ssoToken = ssoToken; } /** * Constructs an instance of AuthContext for a given * organization name, or sub organization name contained in the * single sign on token. * * This constructor should be called for re-authentication of an * authenticated user. single sign on token is the authenticated resource's * Single-Sign-On Token. If the session properties based on * the login method used matches those in the user's new * authenticated session then session upgrade will be done. * If forceAuth flag is true then the existing session * is used and no new session is created otherwise this constructor * behaves same as the constructor with no forceAuth flag. * * Caller would then use login to start the * authentication process and use getRequirements() and * submitRequirements() to pass the credentials * needed for authentication by the plugin authentication modules. * The method getStatus() returns the * authentication status. * * @param ssoToken single sign on token representing the resource's * previous authenticated session. * @param forceAuth indicates that authentication preocess has to be * restarted and given single sign on token will be used and new * session will not be created. * @throws AuthLoginException if AuthContext creation fails. * This exception is kept for backward compatibility only. * * @supported.api */ public AuthContext(SSOToken ssoToken, boolean forceAuth) throws AuthLoginException { this.ssoToken = ssoToken; this.forceAuth = forceAuth; } /** * Starts the login process for the given AuthContext object. * * @exception AuthLoginException if an error occurred during login. * * @supported.api */ public void login() throws AuthLoginException { login(null, null, null, null, null, null); } /** * Starts the login process for the given AuthContext object. * * @param request The HttpServletRequest that was sent to start the authentication process. * @param response The corresponding HttpServletResponse for the HttpServletRequest. * @throws AuthLoginException If an error occurred during login. * * @supported.api */ public void login(HttpServletRequest request, HttpServletResponse response) throws AuthLoginException { login(null, null, null, null, request, response); } /** * Starts the login process for the given AuthContext object * identified by the index type and index name. The IndexType * defines the possible kinds of "objects" or "resources" for which an * authentication can be performed. Currently supported index types are * users, roles, services (or application), levels, resources and * mechanism/authentication modules. * * @param type Authentication index type. * @param indexName Authentication index name. * @exception AuthLoginException if an error occurred during login. * * @supported.api */ public void login(IndexType type, String indexName) throws AuthLoginException { login(type, indexName, null, null, null, null); } /** * Starts the login process for the given AuthContext object * identified by the index type and index name. * The IndexType defines the possible kinds of "objects" * or "resources" for which an authentication can * be performed. Currently supported index types are * users, roles, services (or application), levels, resources and mechanism. * It allows the caller to pass in the desired locale for this request. * * @param type authentication index type * @param indexName authentication index name * @param locale locale setting * * @exception AuthLoginException if an error occurred during login */ public void login(IndexType type, String indexName, String locale) throws AuthLoginException { login(type, indexName, null, null, locale); } /** * Starts the login process for the given AuthContext object * identified by the index type and index name and also completes * the login process by submitting the given User credentials * in the form of Callbacks. * The IndexType defines the possible kinds of "objects" * or "resources" for which an authentication can * be performed. Currently supported index types are * users, roles, services (or application), levels, resources and mechanism. *

* NOTE : This is a simplified wrapper method to eliminate multi-step calls * to 'login' and submit credentials. This method is useful and will work * only for those authentication modules which require only one set of * callbacks or one page. This method can not be used to authenticate to * authentication modules which require user interaction or multiple pages. * * @param type Authentication index type. * @param indexName Authentication index name. * @param userInfo User information/credentials in the form of array of * Callback objects. The Callback objects * array must be in the same order as defined in the authentication * module properties file, otherwise authentication module code will * not work. * @return single-sign-on token for the valid user after successful * authentication. * @exception AuthLoginException if an error occurred during login. */ public SSOToken login(IndexType type, String indexName, Callback[] userInfo) throws AuthLoginException { login(type, indexName, null, null, null, null); SSOToken ssoToken = null; Callback[] callbacks = null; while (hasMoreRequirements()) { callbacks = getRequirements(); if (callbacks != null) { try { submitRequirements(userInfo); } catch (Exception e) { if (authDebug.messageEnabled()) { authDebug.message( "Error: submitRequirements with userInfo : " + e.getMessage()); } throw new AuthLoginException(e); } } } try { if (getStatus() == AuthContext.Status.SUCCESS) { ssoToken = getSSOToken(); } } catch (Exception e) { if (authDebug.messageEnabled()) { authDebug.message("Error: getSSOToken : " + e.getMessage()); } throw new AuthLoginException(e); } return ssoToken; } /** * Starts the login process for the given AuthContext object * identified by the index type and index name with default parameters. * The IndexType defines the possible kinds of "objects" * or "resources" for which an authentication can be performed. Currently * supported index types are users, roles, services (or application), * levels, resources and mechanism/authentication modules. * * @param indexType authentication index type. * @param indexName authentication index name. * @param params contains the default values for the callbacks. The order * of this array matches the callbacks order for this login process. * value for the PasswordCallback is also in String * format, it will be converted to char[] when it is * set to the callback. Internal processing for this string array * uses | as separator. Hence | should not * be used in these default values. Currently only * NameCallback and PasswordCallback are * supported. * @exception AuthLoginException if an error occurred during login. * * @supported.api */ public void login(IndexType indexType, String indexName, String[] params) throws AuthLoginException { login(indexType, indexName, params, null, null, null); } public void login(IndexType indexType, String indexName, String[] params, HttpServletRequest request, HttpServletResponse response) throws AuthLoginException { login(indexType, indexName, params, null, request, response); } /** * Starts the login process for the given AuthContext object * identified by the index type and index name with certain parameters * and environment map. * The IndexType defines the possible kinds of "objects" * or "resources" for which an authentication can be performed. Currently * supported index types are users, roles, services (or application), * levels, modules and resources. * * @param indexType authentication index type. * @param indexName authentication index name. * @param params contains the default values for the callbacks. The order * of this array matches the callbacks order for this login process. * value for the PasswordCallback is also in String * format, it will be converted to char[] when it is * set to the callback. Internal processing for this string array * uses | as separator. Hence | should not * be used in these default values. Currently only * NameCallback and PasswordCallback are * supported. * @param envMap contains the environment key/value pairs. Key is a String * object indicating the property name, value is a Set of String * values for the property. Currenty this parameter only applicable * when the indexTye is AuthContext.IndexType.RESOURCE. * @exception AuthLoginException if an error occurred during login. * * @supported.api */ public void login(IndexType indexType, String indexName, String[] params, Map envMap) throws AuthLoginException { login(indexType, indexName, params, envMap, null, null); } public void login( IndexType indexType, String indexName, String[] params, Map envMap, HttpServletRequest request, HttpServletResponse response ) throws AuthLoginException { if (clientLocale == null) { login(indexType, indexName, params, envMap, null, request, response); } else { String localeStr = clientLocale.toString(); login(indexType, indexName, params, envMap, localeStr, request, response); } } private void login( IndexType indexType, String indexName, String[] params, Map envMap, String locale ) throws AuthLoginException { login(indexType, indexName, params, envMap, locale, null, null); } private void login( IndexType indexType, String indexName, String[] params, Map envMap, String locale, HttpServletRequest request, HttpServletResponse response ) throws AuthLoginException { if (ssoToken != null) { try { organizationName = ssoToken.getProperty( ISAuthConstants.ORGANIZATION); ssoTokenID = ssoToken.getTokenID().toString(); authURL = sessionCache.getSession(new SessionID(ssoTokenID)).getSessionServiceURL(); } catch (Exception e) { throw new AuthLoginException(e); } } if (authURL != null) { authServiceURL = getAuthServiceURL(authURL.getProtocol(), authURL.getHost(), Integer.toString(authURL.getPort()), authURL.getPath()); } AuthLoginException authException = null; try { if (authServiceURL == null) { authServiceURL = getAuthServiceURL( server_proto, server_host, server_port, server_uri); } if (authServiceURL != null) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext.login : runLogin against " + authServiceURL); } runLogin(indexType, indexName, params, envMap, locale, request, response); return; } } catch (AuthLoginException e) { authException = e; authDebug.error("Failed to login to " + authServiceURL); } catch (Exception e) { authDebug.error("Failed to login to " + authServiceURL + ": " + e.getMessage(),e); } if (authURL == null) { // failover when authURL is not specified Vector serviceURLs = null; try { serviceURLs = WebtopNaming.getServiceAllURLs( AuthXMLTags.AUTH_SERVICE); } catch (Exception e) { throw new AuthLoginException(amAuthContext, "loginError", new Object[]{e.getMessage()}); } if (authDebug.messageEnabled()) { authDebug.message("Org Name : " + organizationName); authDebug.message("ssoTokenID: " + ssoTokenID); authDebug.message("serviceURLs: " + serviceURLs); } if (serviceURLs != null) { serviceURLs.remove(authServiceURL); for (Enumeration e = serviceURLs.elements(); e.hasMoreElements(); ) { authServiceURL = (URL)e.nextElement(); try { runLogin(indexType, indexName, params, envMap, locale, request, response); return; } catch (AuthLoginException ex) { authException = ex; authDebug.error("Failed to login in failover with " + authServiceURL + ": " + ex.getMessage()); } } } } authDebug.error("Authentication failed."); if (authException != null) { throw authException; } else { throw new AuthLoginException(amAuthContext, "loginError",null); } } private void runLogin( IndexType indexType, String indexName, String[] params, Map envMap, String locale, HttpServletRequest request, HttpServletResponse response ) throws AuthLoginException { if (!localFlag) { setLocalFlag(authServiceURL); } if (appSSOToken == null) { if (!((indexType == IndexType.MODULE_INSTANCE) && (indexName.equals("Application")))){ appSSOToken = getAppSSOToken(false); } } if (localFlag) { try { if (ssoTokenID == null) { acLocal = com.sun.identity.authentication.service.AuthUtils. getAuthContext(organizationName); } else { if (authDebug.messageEnabled()) { authDebug.message("AuthContext.runLogin: " + "ForceAuth = "+forceAuth); } acLocal = com.sun.identity.authentication.service.AuthUtils. getAuthContext(organizationName, ssoTokenID, false, null, null, null, forceAuth); } LoginState loginState = acLocal.getLoginState(); /* * Set both the HttpRequest and HttpResponse on the login state so they are accessible by the Auth * Modules. */ if (request != null) { loginState.setHttpServletRequest(request); Hashtable hashtable = AuthClientUtils.parseRequestParameters(request); loginState.setParamHash(hashtable); } if (response != null) { loginState.setHttpServletResponse(response); } if (hostName != null) { acLocal.getLoginState().setClient(hostName); } acLocal.login(indexType, indexName, envMap, locale); } catch (AuthException e) { throw new AuthLoginException(e); } if (acLocal.getStatus().equals(Status.SUCCESS)) { onSuccessLocal(); } return; } // Check if 7.0 RR stype protocol needs to be used // This will setup NewAuthContext and authHandles if (useOldStyleRemoteAuthentication) { runRemoteOldAuthContext(); if (loginException != null) { throw loginException; } } // Run Login runRemoteLogin(indexType, indexName, params, envMap, locale, request, response); // reset the retry count retryRunLogin = DEFAULT_RETRY_COUNT; if (authDebug.messageEnabled()) { authDebug.message("useNewStyleRemoteAuthentication : " + useNewStyleRemoteAuthentication); authDebug.message("useOldStyleRemoteAuthentication : " + useOldStyleRemoteAuthentication); authDebug.message("receivedDocument : " + receivedDocument); authDebug.message("loginException : " + loginException); } // If "Login" fails and we have not set 6.3, 7.0 RR style protocol // the server could be either 6.3 or 7.0 RR. Hence try "NewAuthContext" // and then "Login" if (!useNewStyleRemoteAuthentication && !useOldStyleRemoteAuthentication && (receivedDocument == null || (getAuthenticationHandle(receivedDocument)).equals("null")) && loginException != null) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext: trying 6.3 style remote " + "AuthN and setting the flag to use 6.3 style"); } useOldStyleRemoteAuthentication = true; // Server could be either 6.3 or 7.0 RR, try old style // Construct the Request XML with New AuthContext parameters loginException = null; // Reset loginException runRemoteOldAuthContext(); if (loginException != null) { throw loginException; } // Re-try login process with AuthIdentifier runRemoteLogin(indexType, indexName, params, envMap, locale, request, response); // reset the retry count retryRunLogin = DEFAULT_RETRY_COUNT; } else if (!useNewStyleRemoteAuthentication) { useNewStyleRemoteAuthentication = true; } if (loginException != null) { throw loginException; } } private void runRemoteLogin(IndexType indexType, String indexName, String[] params, Map envMap, String locale, HttpServletRequest req, HttpServletResponse res) throws AuthLoginException { try { String xmlString; // remote auth StringBuilder request = new StringBuilder(100); String authHandle = getAuthHandle(); if (ssoTokenID != null && "0".equals(authHandle)) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext.runRemoteLogin: Found SSOTokenID " + ssoTokenID); } authHandle = ssoTokenID; } request.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX, authHandle)); if (appSSOToken != null) { request.append(AuthXMLTags.APPSSOTOKEN_BEGIN); request.append(appSSOToken.getTokenID().toString()); request.append(AuthXMLTags.APPSSOTOKEN_END); } request.append(AuthXMLTags.LOGIN_BEGIN); if (!useOldStyleRemoteAuthentication) { request.append(AuthXMLTags.SPACE) .append(AuthXMLTags.ORG_NAME_ATTR) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append(XMLUtils.escapeSpecialCharacters(organizationName)) .append(AuthXMLTags.QUOTE); if (hostName != null) { request.append(AuthXMLTags.SPACE) .append(AuthXMLTags.HOST_NAME_ATTR) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append(XMLUtils.escapeSpecialCharacters(hostName)) .append(AuthXMLTags.QUOTE); } if (locale != null && !locale.isEmpty()) { request.append(AuthXMLTags.SPACE) .append(AuthXMLTags.LOCALE) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append(XMLUtils.escapeSpecialCharacters(locale)) .append(AuthXMLTags.QUOTE); } if (forceAuth) { request.append(AuthXMLTags.SPACE) .append(AuthXMLTags.FORCE_AUTH_ATTR) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append("true") .append(AuthXMLTags.QUOTE); } } request.append(AuthXMLTags.ELEMENT_END); if (indexType != null) { request.append(AuthXMLTags.INDEX_TYPE_PAIR_BEGIN) .append(AuthXMLTags.SPACE) .append(AuthXMLTags.INDEX_TYPE) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE); if (indexType == IndexType.USER) { request.append(AuthXMLTags.INDEX_TYPE_USER_ATTR); } else if (indexType == IndexType.ROLE) { request.append(AuthXMLTags.INDEX_TYPE_ROLE_ATTR); } else if (indexType == IndexType.SERVICE) { request.append(AuthXMLTags.INDEX_TYPE_SVC_ATTR); } else if (indexType == IndexType.MODULE_INSTANCE) { request.append(AuthXMLTags.INDEX_TYPE_MODULE_ATTR); } else if (indexType == IndexType.LEVEL) { request.append(AuthXMLTags.INDEX_TYPE_LEVEL_ATTR); } else if (indexType == IndexType.COMPOSITE_ADVICE) { request.append(AuthXMLTags.INDEX_TYPE_COMPOSITE_ADVICE_ATTR); } else if (indexType == IndexType.RESOURCE) { request.append(AuthXMLTags.INDEX_TYPE_RESOURCE); } request.append(AuthXMLTags.QUOTE) .append(AuthXMLTags.ELEMENT_END) .append(AuthXMLTags.INDEX_NAME_BEGIN) .append(XMLUtils.escapeSpecialCharacters(indexName)) .append(AuthXMLTags.INDEX_NAME_END) .append(AuthXMLTags.INDEX_TYPE_PAIR_END); } if (locale != null && locale.length() > 0) { request.append(AuthXMLTags.LOCALE_BEGIN); request.append(XMLUtils.escapeSpecialCharacters(locale)); request.append(AuthXMLTags.LOCALE_END); } if (params != null) { StringBuilder paramString = new StringBuilder(); for (int i = 0; i < params.length; i++) { if (i != 0 ) { paramString.append(ISAuthConstants.PIPE_SEPARATOR); } paramString.append(XMLUtils.escapeSpecialCharacters(params[i])); } request.append(AuthXMLTags.PARAMS_BEGIN) .append(paramString.toString()) .append(AuthXMLTags.PARAMS_END); } if (envMap != null && !envMap.isEmpty()) { StringBuilder envString = new StringBuilder(); for (Map.Entry> entry : ((Map>) envMap).entrySet()) { // convert Map to XMLString as follows: // keyname|value1|value2|... String keyName = entry.getKey(); Set values = entry.getValue(); if (values != null && !values.isEmpty()) { envString.append(AuthXMLTags.ENV_AV_BEGIN) .append(AuthClientUtils.escapePipe(XMLUtils.escapeSpecialCharacters(keyName))); for (String value : values) { envString.append(ISAuthConstants.PIPE_SEPARATOR) .append(AuthClientUtils.escapePipe(XMLUtils.escapeSpecialCharacters(value))); } envString.append(AuthXMLTags.ENV_AV_END); } } request.append(AuthXMLTags.ENV_BEGIN) .append(envString.toString()) .append(AuthXMLTags.ENV_END); } request.append(AuthXMLTags.LOGIN_END); if (includeReqRes) { request.append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_START) .append(AuthXMLTags.HTTP_SERVLET_REQUEST_START); String encObj = ""; if (req != null) { try { encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletRequest(req)); } catch (IOException ioe) { authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http request", ioe); } if (authDebug.messageEnabled()) { authDebug.message("req=" + new RemoteHttpServletRequest(req).toString()); } request.append(encObj); } request.append(AuthXMLTags.HTTP_SERVLET_REQUEST_END); request.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_START); if (res != null) { encObj = ""; try { encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletResponse(res)); } catch (IOException ioe) { authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http response", ioe); } if (authDebug.messageEnabled()) { authDebug.message("res=" + res); } request.append(encObj); } request.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_END) .append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_END); } else { if (authDebug.messageEnabled()) { authDebug.message("Not including req/res " + includeReqRes); } } request.append(AuthXMLTags.XML_REQUEST_SUFFIX); xmlString = request.toString(); // process the request, which will check for exceptions // and also get the authentication handle ID receivedDocument = processRequest(xmlString); // Check set the login status checkAndSetLoginStatus(); // if the app token was refreshed, retry remote login if (loginException != null && loginException.getErrorCode().equals(AMAuthErrorCode.REMOTE_AUTH_INVALID_SSO_TOKEN) && retryRunLogin > 0) { retryRunLogin--; if (authDebug.messageEnabled()) { authDebug.message("Run remote login failed due to expired app token, retying"); } // reset as we are starting again loginStatus = Status.IN_PROGRESS; runRemoteLogin(indexType, indexName, params, envMap, locale, req, res); } } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } private void runRemoteOldAuthContext() throws AuthLoginException { try { StringBuilder request = new StringBuilder(100); String[] objs = { "0" }; if (ssoTokenID != null) { objs[0] = ssoTokenID; } request.append(MessageFormat.format( AuthXMLTags.XML_REQUEST_PREFIX, (Object[])objs)) .append(AuthXMLTags.NEW_AUTHCONTEXT_BEGIN) .append(AuthXMLTags.SPACE) .append(AuthXMLTags.ORG_NAME_ATTR) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append(XMLUtils.escapeSpecialCharacters(organizationName)) .append(AuthXMLTags.QUOTE) .append(AuthXMLTags.ELEMENT_END) .append(AuthXMLTags.NEW_AUTHCONTEXT_END) .append(AuthXMLTags.XML_REQUEST_SUFFIX); // process the request, which will check for exceptions // and also get the authentication handle ID receivedDocument = processRequest(request.toString()); // Check set the login status checkAndSetLoginStatus(); } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } /** * Returns the set of Principals or Subject the user has been * authenticated as. * This should be invoked only after successful authentication. * * @return Subject for the authenticated User. * If the authentication fails or the authentication is in process, * this will return null. * * @supported.api */ public Subject getSubject() { if (localFlag) { if (!acLocal.getStatus().equals(Status.SUCCESS)) { return (null); } return (acLocal.getSubject()); } else { if (!loginStatus.equals(Status.SUCCESS)) { return (null); } return (getSubject(receivedDocument)); } } /** * Returns a Map object that * that contains cookies set by AM server * * @return a Map of cookie name and * Cookie object. */ public Map getCookieTable() { return cookieTable; } /** * Returns true if the login process requires more * information from the user to complete the authentication. *

* NOTE: This method has to be called as a condition of a * while loop in order to complete the authentication process * and get the correct Status after submitting the * requirements. * * @return true if more credentials are required from the user. * * @supported.api */ public boolean hasMoreRequirements() { if (localFlag) { return (acLocal.hasMoreRequirements(false)); } else { if ((!loginStatus.equals(Status.IN_PROGRESS)) || ((getCallbacks(receivedDocument, false)) == null)) { return (false); } return (true); } } /** * Returns true if the login process requires more information * from the user to complete the authentication. * * NOTE: This method has to be called as a condition of a while * loop in order to complete the authentication process and get the correct * Status after submitting the requirements. * * @param noFilter flag indicates whether to filter * PagePropertiesCallback or not. Value * true will not filter * PagePropertiesCallback. * @return true if more credentials are required from the user. * * @supported.api */ public boolean hasMoreRequirements(boolean noFilter) { if (localFlag) { return (acLocal.hasMoreRequirements(noFilter)); } else { if ((!loginStatus.equals(Status.IN_PROGRESS)) || ((getCallbacks(receivedDocument, noFilter)) == null)) { return (false); } return (true); } } /** * Returns an array of Callback objects that must be populated * by the user and returned back. These objects are requested by the * authentication plug-ins, and these are usually displayed to the user. * The user then provides the requested information for it to be * authenticated. * * @return an array of Callback objects requesting credentials * from user * * @supported.api */ public Callback[] getRequirements() { if (localFlag) { if (!acLocal.getStatus().equals(Status.IN_PROGRESS)) { return (null); } return (acLocal.getRequirements(false)); } else { if (!loginStatus.equals(Status.IN_PROGRESS)) { return (null); } return (getCallbacks(receivedDocument, false)); } } /** * Returns an array of Callback objects that * must be populated by the user and returned back. * These objects are requested by the authentication plug-ins, * and these are usually displayed to the user. The user then provides * the requested information for it to be authenticated. * * @param noFilter boolean flag indicating whether to filter * PagePropertiesCallback or not. Value true will * not filter PagePropertiesCallback. * * @return an array of Callback objects requesting credentials * from user * * @supported.api */ public Callback[] getRequirements(boolean noFilter) { if (localFlag) { if (!acLocal.getStatus().equals(Status.IN_PROGRESS)) { return (null); } return (acLocal.getRequirements(noFilter)); } else { if (!loginStatus.equals(Status.IN_PROGRESS)) { return (null); } return (getCallbacks(receivedDocument, noFilter)); } } /** * Fetches the remote request from the context * * @return The Http Servlet Request */ public HttpServletRequest getRemoteRequest() { return remoteRequest; } /** * Fetches the remote response from the context * * @return The Http Servlet Response */ public HttpServletResponse getRemoteResponse() { return remoteResponse; } /** * Submits the populated Callback objects to the * authentication plug-in modules. Called after getRequirements * method and obtaining user's response to these requests. * * @param info Array of Callback objects. * * @supported.api */ public void submitRequirements(Callback[] info) { submitRequirements(info, null, null); } public void submitRequirements(Callback[] info, HttpServletRequest request, HttpServletResponse response) { if (authDebug.messageEnabled()) { authDebug.message("submitRequirements with Callbacks : " + info); } if (localFlag) { // Check if we are still in login session if (!acLocal.getStatus().equals(Status.IN_PROGRESS)) { return; } acLocal.submitRequirements(info); if (acLocal.getStatus().equals(Status.SUCCESS)) { onSuccessLocal(); } return; } else { // Check if we are still in login session if (!loginStatus.equals(Status.IN_PROGRESS)) { return; } // Construct the XML try { StringBuilder xml = new StringBuilder(100); String[] authHandles = new String[1]; authHandles[0] = getAuthenticationHandle(receivedDocument); xml.append(MessageFormat.format( AuthXMLTags.XML_REQUEST_PREFIX,(Object[])authHandles)); if (appSSOToken != null) { xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN); xml.append(appSSOToken.getTokenID().toString()). append(AuthXMLTags.APPSSOTOKEN_END); } xml.append(AuthXMLTags.SUBMIT_REQS_BEGIN) .append(AuthXMLUtils.getXMLForCallbacks(info)); if (clientLocale != null) { String localeStr = clientLocale.toString(); if ((localeStr != null) && (localeStr.length() > 0)) { xml.append(AuthXMLTags.LOCALE_BEGIN) .append(XMLUtils.escapeSpecialCharacters(localeStr)) .append(AuthXMLTags.LOCALE_END); } } xml.append(AuthXMLTags.SUBMIT_REQS_END); if (includeReqRes) { // serialized request and response objects xml.append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_START) .append(AuthXMLTags.HTTP_SERVLET_REQUEST_START); String encObj = ""; if (request != null) { try { encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletRequest(request)); } catch (IOException ioe) { authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http request", ioe); } if (authDebug.messageEnabled()) { authDebug.message("req=" + request); } xml.append(encObj); } xml.append(AuthXMLTags.HTTP_SERVLET_REQUEST_END); xml.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_START); if (response != null) { encObj = ""; try { encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletResponse(response)); } catch (IOException ioe) { authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http response", ioe); } if (authDebug.messageEnabled()) { authDebug.message("res=" + response); } xml.append(encObj); } xml.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_END) .append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_END); } xml.append(AuthXMLTags.XML_REQUEST_SUFFIX); // Send the request to be processes receivedDocument = processRequest(xml.toString()); // Check set the login status checkAndSetLoginStatus(); } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } } /** * Logs out the user and also invalidates the single sign on token * associated with this AuthContext. * * @throws AuthLoginException if an error occurred during logout. * * @supported.api */ public void logout() throws AuthLoginException { if (localFlag) { acLocal.logout(); return; } // Construct the XML try { StringBuilder xml = new StringBuilder(100); String[] authHandles = new String[1]; authHandles[0] = getAuthenticationHandle(receivedDocument); xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX, (Object[])authHandles)); if (appSSOToken != null) { xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN); xml.append(appSSOToken.getTokenID().toString()). append(AuthXMLTags.APPSSOTOKEN_END); } xml.append(AuthXMLTags.LOGOUT_BEGIN) .append(AuthXMLTags.LOGOUT_END) .append(AuthXMLTags.XML_REQUEST_SUFFIX); // Send the request to be processes receivedDocument = processRequest(xml.toString()); // Check set the login status checkAndSetLoginStatus(); } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } /** * Logs out the user and also invalidates the single sign on token * associated with this AuthContext. * * This method causes the logout to happen on the server and the * correct SPI hooks to be called. * * @throws AuthLoginException if an error occurred during logout. * * @supported.api */ public void logoutUsingTokenID() throws AuthLoginException { if (localFlag) { return; } if (ssoToken != null) { try { organizationName = ssoToken.getProperty( ISAuthConstants.ORGANIZATION); ssoTokenID = ssoToken.getTokenID().toString(); authURL = sessionCache.getSession(new SessionID(ssoTokenID)).getSessionServiceURL(); } catch (Exception e) { throw new AuthLoginException(e); } } if (authURL != null) { authServiceURL = getAuthServiceURL(authURL.getProtocol(), authURL.getHost(), Integer.toString(authURL.getPort()), authURL.getPath()); } // Construct the XML try { StringBuilder xml = new StringBuilder(100); String[] authHandles = new String[1]; authHandles[0] = ssoToken.getTokenID().toString(); xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX, (Object[]) authHandles)); if (appSSOToken != null) { xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN); xml.append(appSSOToken.getTokenID().toString()). append(AuthXMLTags.APPSSOTOKEN_END); } xml.append(AuthXMLTags.LOGOUT_BEGIN) .append(AuthXMLTags.LOGOUT_END) .append(AuthXMLTags.XML_REQUEST_SUFFIX); // Send the request to be processes receivedDocument = processRequest(xml.toString()); // Check set the login status checkAndSetLoginStatus(); } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } /** * Returns login exception, if any, during the authentication process. * Typically set when the login fails. * * @return login exception. * @supported.api */ public AuthLoginException getLoginException() { if (localFlag) { return (acLocal.getLoginException()); } else { return (loginException); } } /** * Returns the Single-Sign-On (SSO) Token for the authenticated * user. If the user has not successfully authenticated * Exception will be thrown. *

* Single sign token can be used as the authenticated token. * * @return Single-Sign-On token for the valid user after successful * authentication. * @throws L10NMessageImpl if the user is not authenticated or an error is * encountered in retrieving the user's single sign on token. * @supported.api */ public SSOToken getSSOToken() throws L10NMessageImpl { if (localFlag) { if (!acLocal.getStatus().equals(Status.SUCCESS)) { throw new L10NMessageImpl( amAuthContext, "statusNotSuccess", null); } return (acLocal.getSSOToken()); } else { // Get the loginStatus node if (!loginStatus.equals(Status.SUCCESS)) { throw new L10NMessageImpl( amAuthContext, "statusNotSuccess", null); } Node loginStatusNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.LOGIN_STATUS); if (loginStatusNode == null) { throw new L10NMessageImpl(amAuthContext, "noStatusNode", null); } String ssoTokenIDTmp = XMLUtils.getNodeAttributeValue(loginStatusNode, AuthXMLTags.SSOTOKEN); try { return new com.iplanet.sso.providers.dpro.SSOProviderImpl(). createSSOToken(ssoTokenIDTmp, true); } catch (SSOException ssoe) { throw new L10NMessageImpl( amAuthContext, "createSSOTokenError", null); } } } /** * Returns the current status of the authentication process as * AuthContext.Status. * * @return Status of the authentication process. * * @supported.api */ public Status getStatus() { if (localFlag) { return (acLocal.getStatus()); } else { return (loginStatus); } } /** * Returns the current Auth Identifier of the authentication * process as String Session ID. * * @return Auth Identifier of the authentication process. */ public String getAuthIdentifier() { if (localFlag) { return (acLocal.getAuthIdentifier()); } else { return (getAuthHandle()); } } /** * Returns the Successful Login URL for the authenticated user. * * @return the Successful Login URL for the authenticated user. * @throws Exception if it fails to get url for auth success */ public String getSuccessURL() throws Exception { if (localFlag) { if (!acLocal.getStatus().equals(Status.SUCCESS)) { throw new L10NMessageImpl(amAuthContext, "statusNotSuccess", null); } return (acLocal.getSuccessURL()); } else { // Get the loginStatus node if (!loginStatus.equals(Status.SUCCESS)) { throw new L10NMessageImpl(amAuthContext, "statusNotSuccess", null); } Node loginStatusNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.LOGIN_STATUS); if (loginStatusNode == null) { throw new L10NMessageImpl(amAuthContext, "noStatusNode", null); } return (XMLUtils.getNodeAttributeValue(loginStatusNode, AuthXMLTags.SUCCESS_URL)); } } /** * Returns the Failure Login URL for the authenticating user. * * @return the Failure Login URL for the authenticating user * @throws Exception if it fails to get url for auth failure */ public String getFailureURL() throws Exception { if (localFlag) { return (acLocal.getFailureURL()); } else { // Get the loginStatus node Node loginStatusNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.LOGIN_STATUS); if (loginStatusNode == null) { throw new L10NMessageImpl(amAuthContext, "noStatusNode", null); } return (XMLUtils.getNodeAttributeValue(loginStatusNode, AuthXMLTags.FAILURE_URL)); } } /** * Resets this instance of AuthContext object, so that a new * login process can be initiated. A new authentication process can started * using any one of the login methods. */ public void reset() { loginStatus = Status.NOT_STARTED; //organizationName = null; //receivedDocument = null; //loginException = null; } /** * Returns the the organization name that was set during the * AuthContext constructor. * * @return Organization name in the AuthContext. * * @supported.api */ public String getOrganizationName() { return (this.organizationName); } /** * * Returns authentication module/s instances (or plugins) configured * for a organization, or sub-organization name that was set during the * AuthContext constructor. * * @return Set of Module instance names. * * @supported.api */ public Set getModuleInstanceNames() { if (authURL != null) { authServiceURL = getAuthServiceURL( authURL.getProtocol(), authURL.getHost(), Integer.toString(authURL.getPort()), authURL.getPath()); } if (!localFlag) { setLocalFlag(authServiceURL); } if (localFlag) { return (acLocal.getModuleInstanceNames()); } else { if (authServiceURL == null) { try { authServiceURL = getAuthServiceURL(server_proto, server_host, server_port, server_uri); } catch (Exception e) { return Collections.EMPTY_SET; } } sendQueryInformation(AuthXMLTags.MODULE_INSTANCE); //Receive data Node queryResultNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.QUERY_RESULT); if (queryResultNode == null) { return (null); } // Iteratate through moduleInstanceNames HashSet moduleInstanceNames = new HashSet(); NodeList childNodes = queryResultNode.getChildNodes(); if ( childNodes != null ) { for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); String moduleName = XMLUtils.getValueOfValueNode(childNode); moduleInstanceNames.add(moduleName); } } return (moduleInstanceNames); } } /** * Terminates an ongoing login call that has not yet completed. * * @exception AuthLoginException if an error occurred during abort. * * @supported.api */ public void abort() throws AuthLoginException { if (localFlag) { acLocal.abort(); return; } // Construct the XML try { StringBuilder xml = new StringBuilder(100); String[] authHandles = new String[1]; authHandles[0] = getAuthenticationHandle(receivedDocument); xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX, (Object[])authHandles)); if (appSSOToken != null) { xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN); xml.append(appSSOToken.getTokenID().toString()). append(AuthXMLTags.APPSSOTOKEN_END); } xml.append(AuthXMLTags.ABORT_BEGIN) .append(AuthXMLTags.ABORT_END) .append(AuthXMLTags.XML_REQUEST_SUFFIX); // Send the request to be processes receivedDocument = processRequest(xml.toString()); // Check set the login status checkAndSetLoginStatus(); } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } /** * Sets the password for the certificate database. * It is required to call only once to initialize certificate database if * the password is not set in the password file (specified as * the value for com.iplanet.am.admin.cli.certdb.passfile * in AMConfig.properties). If both are set, this method will * overwrite the value in certificate password file. * * @param password Password for the certificate database. * * @supported.api */ public static void setCertDBPassword(String password) { try { if (usingJSSEHandler) { Class pcbClass = (Class) Class.forName(JSSE_PASSWORD_CALLBACK); Object passwdCallback = (Object) pcbClass.newInstance(); Method method = pcbClass.getMethod("setPassword", new Class[] { String.class }); KeyStore keystore = (KeyStore)method.invoke( passwdCallback, new Object[] { password }); } else { Class initializer = Class.forName(JSS_PASSWORD_UTIL); Constructor initializerConstructor = initializer.getConstructor( new Class[] { String.class }); initializerConstructor.newInstance(new Object[] { password }); } } catch (Exception e) { e.printStackTrace(); authDebug.message("Error in setCertDBPassword : " + e.getMessage()); } } /** * Returns the error template. * * @return error template. */ public String getErrorTemplate() { if (localFlag) { return (acLocal.getErrorTemplate()); } else { if (receivedDocument == null) { //something went terribly wrong, let's return with internal error template return AuthClientUtils.getErrorTemplate(AMAuthErrorCode.AUTH_ERROR); } String errTemplate = ""; Node exceptionNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.EXCEPTION); if (exceptionNode != null) { errTemplate = XMLUtils.getNodeAttributeValue(exceptionNode, AuthXMLTags.TEMPLATE_NAME); } return errTemplate; } } /** * Returns the error message. * * @return error message. */ public String getErrorMessage() { if (localFlag) { return (acLocal.getErrorMessage()); } else { if (receivedDocument == null) { //something went terribly wrong, let's return with internal error message return AuthClientUtils.getErrorMessage(AMAuthErrorCode.AUTH_ERROR); } String errMessage = null; Node exceptionNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.EXCEPTION); if (exceptionNode != null) { errMessage = XMLUtils.getNodeAttributeValue(exceptionNode, AuthXMLTags.MESSAGE); } return errMessage; } } /** * Returns error code. * * @return error code with white space trimmed */ public String getErrorCode() { if (localFlag) { return (acLocal.getErrorCode()); } else { if (receivedDocument == null) { //something went terribly wrong return AMAuthErrorCode.AUTH_ERROR; } String errCode = ""; Node exceptionNode = XMLUtils.getRootNode(receivedDocument, AuthXMLTags.EXCEPTION); if (exceptionNode != null) { errCode = XMLUtils.getNodeAttributeValue(exceptionNode, AuthXMLTags.ERROR_CODE); } if (errCode != null) { return errCode.trim(); } else { return errCode; } } } /** * Sets the client's hostname or IP address.This could be used * by the policy component to restrict access to resources. * This method is ineffective if the "Remote Auth Security" option under * the global configuration of Core Authentication Service is not enabled. * This method must be called before calling login method. * If it is called after calling login then * it is ineffective. * * @param hostname hostname or ip address * * @supported.api */ public void setClientHostName(String hostname) { this.hostName = hostname; } /** * Returns the client's hostname or IP address as set by * setClientHostName * * @return hostname/IP address * * @supported.api */ public String getClientHostName() { return (hostName); } /** * Sets locale based on user locale preferemce. * * @param loc locale preference of user */ public void setLocale (java.util.Locale loc) { clientLocale = loc; } /** * Returns locale preference set in AuthConext * @return - user prefered locale. */ public java.util.Locale getLocale () { return clientLocale; } private AuthLoginException checkException(){ AuthLoginException exception = null; String error = getErrorCode(); // if the app token is invalid, refresh the token if (error != null && error.equals(AMAuthErrorCode.REMOTE_AUTH_INVALID_SSO_TOKEN)) { appSSOToken = getAppSSOToken(true); } if (error != null && error.length() != 0){ exception = new AuthLoginException("amAuth", error, null); } else { error = getErrorMessage(); if (error != null && error.length() != 0) { exception = new AuthLoginException(error); } } return exception; } protected void checkAndSetLoginStatus(){ Node loginStatusNode = XMLUtils.getRootNode( receivedDocument, AuthXMLTags.LOGIN_STATUS); if (loginStatusNode == null) { loginException = checkException(); if (includeReqRes) { remoteRequest = AuthXMLUtils.getRemoteRequest( XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE)); remoteResponse = AuthXMLUtils.getRemoteResponse( XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE)); } } else { //since there was no error, we should reset the loginException, to handle the case when the first auth //server was not available. loginException = null; // Get the status attribute String status = XMLUtils.getNodeAttributeValue( loginStatusNode, AuthXMLTags.STATUS); if (status != null) { if (status.equals(Status.SUCCESS.toString())) { loginStatus = Status.SUCCESS; } else if (status.equals(Status.FAILED.toString())) { loginStatus = Status.FAILED; loginException = checkException(); } else if (status.equals(Status.COMPLETED.toString())) { loginStatus = Status.COMPLETED; } else if (status.equals(Status.IN_PROGRESS.toString())) { loginStatus = Status.IN_PROGRESS; } else if (status.equals(Status.RESET.toString())) { loginStatus = Status.RESET; } } if (includeReqRes) { remoteRequest = AuthXMLUtils.getRemoteRequest( XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE)); remoteResponse = AuthXMLUtils.getRemoteResponse( XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE)); } if (authDebug.messageEnabled()) { authDebug.message("LoginStatus : " + loginStatus); } } } protected void sendQueryInformation(String reqInfo) { // Construct the XML try { StringBuilder xml = new StringBuilder(100); String[] authHandles = new String[1]; authHandles[0] = getAuthHandle(); xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX, (Object[])authHandles)); if (appSSOToken != null) { xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN); xml.append(appSSOToken.getTokenID().toString()). append(AuthXMLTags.APPSSOTOKEN_END); } xml.append(AuthXMLTags.QUERY_INFO_BEGIN) .append(AuthXMLTags.SPACE) .append(AuthXMLTags.REQUESTED_INFO) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append(reqInfo) .append(AuthXMLTags.QUOTE); if (authHandles[0].equals("0")) { xml.append(AuthXMLTags.SPACE) .append(AuthXMLTags.ORG_NAME_ATTR) .append(AuthXMLTags.EQUAL) .append(AuthXMLTags.QUOTE) .append(XMLUtils.escapeSpecialCharacters(organizationName)) .append(AuthXMLTags.QUOTE); } xml.append(AuthXMLTags.ELEMENT_END) .append(AuthXMLTags.QUERY_INFO_END) .append(AuthXMLTags.XML_REQUEST_SUFFIX); // Send the request to be processes receivedDocument = processRequest(xml.toString()); // Check set the login status checkAndSetLoginStatus(); } catch (AuthLoginException le) { // Login has failed loginStatus = Status.FAILED; loginException = le; } } private void setLocalFlag(URL url) { try { String urlStr = url.getProtocol() + "://" + url.getHost() + ":" + Integer.toString(url.getPort()); if (authDebug.messageEnabled()) { authDebug.message("in setLocalFlag(), url : " + urlStr); authDebug.message("AuthContext.localAuthServiceID : " + localAuthServiceID); } if ((localAuthServiceID != null) && (urlStr.equalsIgnoreCase(localAuthServiceID)) ) { localFlag = true; } } catch (Exception e) { authDebug.error("AuthContext::setLocalFlag:: " + e); } } protected Document processRequest(String xmlRequest) throws AuthLoginException { Document doc = null; try { Request request = new Request(xmlRequest); RequestSet set = new RequestSet(AuthXMLTags.AUTH_SERVICE); set.addRequest(request); URL url = authServiceURL; if (url.getProtocol().equals("https") && (nickName != null)) { Class[] paramtype = {String.class}; Object[] param = {nickName}; String protHandler = protHandlerPkg + ".https.Handler"; Constructor construct = Class.forName(protHandler).getConstructor(paramtype); URLStreamHandler handler = (URLStreamHandler)construct.newInstance(param); url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile(), handler); } if (authDebug.messageEnabled()) { authDebug.message("Service URL : " + url.toString()); } Vector responses = PLLClient.send(url, set, cookieTable); if ((responses.isEmpty()) || (responses.size() != 1)) { throw new L10NMessageImpl(amAuthContext, "responseError", null); } Response res = (Response) responses.elementAt(0); String responseStr = (String)res.getContent(); doc = XMLUtils.getXMLDocument( new ByteArrayInputStream(responseStr.getBytes("UTF-8"))); } catch (Exception e) { authDebug.message("error in getting service url", e); throw new AuthLoginException(amAuthContext, "xmlProcessError", null, e); } return (doc); } protected static void checkForException(Document document) throws AuthLoginException { Node exceptionNode = XMLUtils.getRootNode( document, AuthXMLTags.EXCEPTION); if (exceptionNode != null) { throw (new AuthLoginException(XMLUtils.getNodeAttributeValue( exceptionNode, AuthXMLTags.MESSAGE))); } } protected String getAuthenticationHandle(Document document) throws AuthLoginException { Node responseNode = XMLUtils.getRootNode( document, AuthXMLTags.RESPONSE); if (responseNode == null) { throw new AuthLoginException(amAuthContext, "responseError", null); } String authID = XMLUtils.getNodeAttributeValue( responseNode, AuthXMLTags.AUTH_ID_HANDLE); return (authID); } protected static Callback[] getCallbacks( Document document, boolean noFilter) { return (AuthXMLUtils.getCallbacks(XMLUtils.getRootNode(document, AuthXMLTags.CALLBACKS), noFilter)); } protected static Subject getSubject(Document document) { Node loginStatusNode = XMLUtils.getRootNode(document, AuthXMLTags.LOGIN_STATUS); if (loginStatusNode == null) { return (null); } Node subjectNode = XMLUtils.getChildNode(loginStatusNode, AuthXMLTags.SUBJECT); if (subjectNode == null) { return (null); } String subject = XMLUtils.getValueOfValueNode(subjectNode); try { Subject sSubject = AuthXMLUtils.getDeSerializedSubject(subject); if (authDebug.messageEnabled()) { authDebug.message("Deserialized subject : " + sSubject.toString()); } return sSubject; } catch (Exception e) { authDebug.message("get Deserialized subject error : " , e); return null; } } protected static String getXMLforSubject(Subject subject) { if (subject == null) { return (""); } StringBuilder request = new StringBuilder(100); request.append(AuthXMLTags.SUBJECT_BEGIN); String serializeSubject = AuthXMLUtils.getSerializedSubject(subject); request.append(serializeSubject); request.append(AuthXMLTags.SUBJECT_END); return (request.toString()); } /** * Returns the account lockout message. This can be either a dynamic * message indicating the number of tries left or the the account * deactivated message. * * @return account lockout message. */ public String getLockoutMsg() { String lockoutMsg = null; if (localFlag) { lockoutMsg = acLocal.getLockoutMsg(); } else { // Account Lockout Warning Check by scanning the error // message in the exception thrown by the server lockoutMsg = getErrorMessage(); if((lockoutMsg == null) || (lockoutMsg.indexOf("Account lockout") == -1)){ lockoutMsg = ""; } } return lockoutMsg; } /** * Returns true if account is lock out. * * @return true if account is lock out. */ public boolean isLockedOut() { boolean isLockedOut = false; if (localFlag) { isLockedOut = acLocal.isLockedOut(); } else { // TBD } return isLockedOut; } /** * The class Status defines the possible * authentication states during the login process. * * @supported.all.api */ public static class Status extends Object { private String status; /** * The NOT_STARTED status indicates that the login process * has not yet started. Basically, it means that the method * login has not been called. */ public static final Status NOT_STARTED = new Status("not_started"); /** * The IN_PROGRESS status indicates that the login process * is in progress. Basically, it means that the login * method has been called and that this object is waiting for the user * to send authentication information. */ public static final Status IN_PROGRESS = new Status("in_progress"); /** * * The SUCCESS indicates that the login process has * succeeded. */ public static final Status SUCCESS = new Status("success"); /** * The FAILED indicates that the login process has failed. */ public static final Status FAILED = new Status("failed"); /** * * The COMPLETED indicates that the user has been * successfully logged out. */ public static final Status COMPLETED = new Status("completed"); /** * The RESET indicates that the login process has been * reset or re-initialized. */ public static final Status RESET = new Status("reset"); /** * The ORG_MISMATCH indicates that the framework * org and the org required by the user do * not match. */ public static final Status ORG_MISMATCH = new Status("org_mismatch"); private Status() { // do nothing } private Status(String s) { status = s; } /** * Returns the string representation of the authentication status. * * @return String representation of authentication status. */ public String toString() { return (status); } /** * Checks if two authentication status objects are equal. * * @param authStatus Reference object with which to compare. * @return true if the objects are same. */ public boolean equals(Object authStatus) { if (authStatus instanceof Status) { Status s = (Status) authStatus; return (s.status.equalsIgnoreCase(status)); } return (false); } } /** * The class IndexType defines the possible kinds of "objects" * or "resources" for which an authentication can be performed. * * @supported.all.api */ public static class IndexType extends Object { private String index; /** * The USER index type indicates that the index name given * corresponds to a user. */ public static final IndexType USER = new IndexType("user"); /** * The ROLE index type indicates that the index name given * corresponds to a role. */ public static final IndexType ROLE = new IndexType("role"); /** * * The SERVICE index type indicates that the index name * given corresponds to a service (or application). */ public static final IndexType SERVICE = new IndexType("service"); /** * The LEVEL index type indicates that the index name * given corresponds to a given authentication level. */ public static final IndexType LEVEL = new IndexType("level"); /** * The MODULE_INSTANCE index type indicates that the index * name given corresponds to one of the authentication modules. */ public static final IndexType MODULE_INSTANCE = new IndexType("module_instance"); /** * The RESOURCE index type indicates that the index * name given corresponds to a given policy protected resource URL. */ public static final IndexType RESOURCE = new IndexType("resource"); /** * The COMPOSITE_ADVICE index type indicates that the * index name given corresponds to string in the form of XML * representing different Policy Authentication conditions, example * AuthSchemeCondition, AuthLevelCondition, * etc. */ public static final IndexType COMPOSITE_ADVICE = new IndexType("composite_advice"); private IndexType() { // do nothing } private IndexType(String s) { index = s; } /** * Returns the string representation of the index type. * * @return String representation of index type. */ public String toString() { return (index); } /** * Checks if two index type objects are equal. * * @param indexType Reference object with which to compare. * * @return true if the objects are same. */ public boolean equals(Object indexType) { if (indexType instanceof IndexType) { IndexType s = (IndexType) indexType; return (s.index.equalsIgnoreCase(index)); } return (false); } } private String getAuthHandle() { String handle = null; if (receivedDocument != null) { try { handle = getAuthenticationHandle(receivedDocument); } catch (Exception e) { // do nothing } } if ( handle == null ) { handle = "0"; } return handle; } private static URL getAuthServiceURL( String protocol, String host, String port, String uri ) { URL authservice = null; try { authservice = WebtopNaming.getServiceURL(AuthXMLTags.AUTH_SERVICE, protocol, host, port, uri); } catch (Exception e) { authDebug.error("Failed to obtain auth service url from server: " + protocol + "://" + host + ":" + port); } return authservice; } private void onSuccessLocal() { if (localSessionChecked) { return; } SSOToken currToken = acLocal.getSSOToken(); com.iplanet.dpro.session.service.InternalSession oldSess = acLocal.getLoginState().getOldSession(); if (oldSess != null) { if (forceAuth) { try { SSOTokenManager.getInstance(). destroyToken(currToken); } catch (SSOException ssoExp) { authDebug.error("AuthContext.onSuccessLocal: ", ssoExp); } acLocal.getLoginState().setSession(oldSess); acLocal.getLoginState().setForceAuth(false); ssoToken = acLocal.getSSOToken(); ssoTokenID = ssoToken.getTokenID().toString(); } else { InjectorHolder.getInstance(SessionService.class).destroyInternalSession(oldSess.getID()); } } localSessionChecked = true; } /** * Returns the application sso token. Can perform a check to ensure that * the app token is still valid (requires a session refresh call to OpenAM) * * @param refresh true if we should check with OpenAM if the app token is valid * @return a valid application's sso token. */ private SSOToken getAppSSOToken(boolean refresh) { SSOToken appToken = null; try { appToken = (SSOToken) AccessController.doPrivileged( AdminTokenAction.getInstance()); } catch (AMSecurityPropertiesException aspe) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext::getAppSSOToken: " + "unable to get app ssotoken " + aspe.getMessage()); } } if (refresh) { // ensure the token is valid try { SSOTokenManager ssoTokenManager = SSOTokenManager.getInstance(); ssoTokenManager.refreshSession(appToken); if (!ssoTokenManager.isValidToken(appToken)) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext.getAppSSOToken(): " + "App SSOToken is invalid, retrying"); } try { appToken = (SSOToken) AccessController.doPrivileged( AdminTokenAction.getInstance()); } catch (AMSecurityPropertiesException aspe) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext::getAppSSOToken: " + "unable to get app ssotoken " + aspe.getMessage()); } } } } catch (SSOException ssoe) { if (authDebug.messageEnabled()) { authDebug.message("AuthContext.getAppSSOToken(): " + "unable to refresh app token: " + ssoe.getL10NMessage()); } try { appToken = (SSOToken) AccessController.doPrivileged( AdminTokenAction.getInstance()); } catch (AMSecurityPropertiesException aspe) { if (authDebug.errorEnabled()) { authDebug.error("AuthContext::getAppSSOToken: " + "unable to get app ssotoken " + aspe.getMessage()); } } } } if (authDebug.messageEnabled()) { if (appToken == null) { authDebug.message("Null App SSO Token"); } else { authDebug.message("Obtained App Token= " + appToken.getTokenID().toString()); } } return appToken; } public AuthContextLocal getAuthContextLocal() { return acLocal; } }