001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: AuthContext.java,v 1.25 2009/11/21 01:12:59 qcheng Exp $
026 *
027 * Portions Copyrighted 2010-2016 ForgeRock AS.
028 */
029package com.sun.identity.authentication;
030
031import com.iplanet.am.util.SystemProperties;
032import com.iplanet.dpro.session.SessionID;
033import com.iplanet.dpro.session.service.SessionService;
034import com.iplanet.services.comm.client.PLLClient;
035import com.iplanet.services.comm.share.Request;
036import com.iplanet.services.comm.share.RequestSet;
037import com.iplanet.services.comm.share.Response;
038import com.iplanet.services.naming.WebtopNaming;
039import com.iplanet.sso.SSOException;
040import com.iplanet.sso.SSOToken;
041import com.iplanet.sso.SSOTokenManager;
042import com.sun.identity.authentication.client.AuthClientUtils;
043import com.sun.identity.authentication.server.AuthContextLocal;
044import com.sun.identity.authentication.service.AMAuthErrorCode;
045import com.sun.identity.authentication.service.AuthException;
046import com.sun.identity.authentication.service.LoginState;
047import com.sun.identity.authentication.share.AuthXMLTags;
048import com.sun.identity.authentication.share.AuthXMLUtils;
049import com.sun.identity.authentication.spi.AuthLoginException;
050import com.sun.identity.authentication.util.ISAuthConstants;
051import com.sun.identity.security.AMSecurityPropertiesException;
052import com.sun.identity.security.AdminTokenAction;
053import com.sun.identity.shared.Constants;
054import com.sun.identity.shared.debug.Debug;
055import com.sun.identity.shared.locale.L10NMessageImpl;
056import com.sun.identity.shared.xml.XMLUtils;
057import org.forgerock.guice.core.InjectorHolder;
058import org.forgerock.openam.authentication.service.protocol.RemoteHttpServletRequest;
059import org.forgerock.openam.authentication.service.protocol.RemoteHttpServletResponse;
060import org.forgerock.openam.session.SessionCache;
061import org.w3c.dom.Document;
062import org.w3c.dom.Node;
063import org.w3c.dom.NodeList;
064
065import javax.security.auth.Subject;
066import javax.security.auth.callback.Callback;
067import javax.servlet.http.HttpServletRequest;
068import javax.servlet.http.HttpServletResponse;
069import java.io.ByteArrayInputStream;
070import java.io.IOException;
071import java.lang.reflect.Constructor;
072import java.lang.reflect.Method;
073import java.net.URL;
074import java.net.URLStreamHandler;
075import java.security.AccessController;
076import java.security.KeyStore;
077import java.text.MessageFormat;
078import java.util.Collections;
079import java.util.Enumeration;
080import java.util.HashMap;
081import java.util.HashSet;
082import java.util.Hashtable;
083import java.util.Map;
084import java.util.ResourceBundle;
085import java.util.Set;
086import java.util.Vector;
087
088/**
089 * The <code>AuthContext</code> provides the implementation for
090 * authenticating users.
091 * <p>
092 * A typical caller instantiates this class and starts the login process.
093 * The caller then obtains an array of <code>Callback</code> objects,
094 * which contains the information required by the authentication plug-in
095 * module. The caller requests information from the user. On receiving
096 * the information from the user, the caller submits the same to this class.
097 * While more information is required, the above process continues until all
098 * the information required by the plug-ins/authentication modules, has
099 * been supplied. The caller then checks if the user has successfully
100 * been authenticated. If successfully authenticated, the caller can
101 * then get the <code>Subject</code> and <code>SSOToken</code> for the user;
102 * if not successfully authenticated, the caller obtains the
103 * <code>AuthLoginException</code>.
104 * <p>
105 * The implementation supports authenticating users either locally
106 * i.e., in process with all authentication modules configured or remotely
107 * to an authentication service/framework. (See documentation to configure
108 * in either of the modes).
109 *
110 * @supported.api
111 */
112public class AuthContext extends Object implements java.io.Serializable {
113
114    private java.util.Locale clientLocale = null;
115    
116    private String server_proto =
117        SystemProperties.get(Constants.AM_SERVER_PROTOCOL);
118    private String server_host  =
119        SystemProperties.get(Constants.AM_SERVER_HOST);
120    private String server_port  =
121        SystemProperties.get(Constants.AM_SERVER_PORT);
122    private String server_uri  =
123        SystemProperties.get(Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR);
124    private boolean includeReqRes  =
125        SystemProperties.getAsBoolean(Constants.REMOTEAUTH_INCLUDE_REQRES);
126
127    private static final String amAuthContext = "amAuthContext";
128    
129    private static final String JSS_PASSWORD_UTIL =
130        "com.sun.identity.authentication.util.JSSPasswordUtil";
131    
132    private static final String JSSE_PASSWORD_CALLBACK =
133        "com.sun.identity.security.keystore.AMCallbackHandler";
134    
135    static String protHandlerPkg =
136        System.getProperty(Constants.PROTOCOL_HANDLER, Constants.JSSE_HANDLER);
137    
138    static boolean usingJSSEHandler =
139        protHandlerPkg.equals(Constants.JSSE_HANDLER);
140    
141    // Debug & I18N class
142    protected static Debug authDebug = Debug.getInstance(amAuthContext);
143    protected static ResourceBundle bundle =
144            com.sun.identity.shared.locale.Locale.getInstallResourceBundle(amAuthContext);
145    
146    Status loginStatus = Status.IN_PROGRESS;
147    String organizationName = "";
148    Document receivedDocument;
149    AuthLoginException loginException = null;
150
151    String hostName = null;
152    private boolean forceAuth = false;
153    private boolean localSessionChecked = false;
154    String nickName = null;
155    private URL authURL = null;
156    private URL authServiceURL = null;
157    private SSOToken ssoToken = null;
158    private String ssoTokenID = null;
159    private static SSOToken appSSOToken = null;
160    com.sun.identity.authentication.server.AuthContextLocal acLocal = null;
161    private final static int DEFAULT_RETRY_COUNT = 1;
162    private int retryRunLogin = DEFAULT_RETRY_COUNT;
163    
164    /**
165     * Variables for checking auth service is running local
166     */ 
167    public boolean localFlag = false;
168    /**
169     * Variables for local AuthService identifier
170     */ 
171    public static String localAuthServiceID;
172
173    // Variable to check if 6.3 style remote AuthN has to be performed
174    static boolean useOldStyleRemoteAuthentication;
175    static boolean useNewStyleRemoteAuthentication;
176    
177    // this cookieTable is used to keep all the cookies retrieved from the
178    // the PLL layer and replay them in subsequent auth requests, mainly for
179    // persistence purpose.
180    private HashMap cookieTable = new HashMap();
181
182    private HttpServletRequest remoteRequest = null;
183    private HttpServletResponse remoteResponse = null;
184
185    private final SessionCache sessionCache = SessionCache.getInstance();
186    
187    /**
188     * Constructs an instance of <code>AuthContext</code> for a given
189     * organization name or sub organization name. This organization or
190     * sub-organization name must be either "/" separated
191     * ( where it starts with "/" ) , DN , Domain name or DNS Alias Name.
192     * <p>
193     * Caller would then use <code>login</code> to start the
194     * authentication process and use <code>getRequirements()</code> and
195     * <code>submitRequirements()</code> to pass the credentials
196     * needed for authentication by the plugin authentication modules.
197     * The method <code>getStatus()</code> returns the
198     * authentication status.
199     *
200     * @param orgName Name of the user's organization.
201     * @throws AuthLoginException if <code>AuthContext</code> creation fails.
202     *         This exception is kept for backward compatibility only.
203     * 
204     * @supported.api
205     */
206    public AuthContext(String orgName) throws AuthLoginException {
207        organizationName = orgName;
208    }
209    
210    /**
211     * Constructs an instance of <code>AuthContext</code> for a given
212     * organization name, or sub organization name and the OpenAM server
213     * URL.
214     * This organization or sub-organization name must be either "/" separated
215     * ( where it starts with "/" ) , DN , Domain name or DNS Alias Name.
216     * And the <code>url</code> should specify the OpenAM server's protocol,
217     * host name, and port number,
218     * for example : <code>http://daye.red.iplanet.com:58080</code>
219     *
220     * Caller would then use <code>login</code> to start the
221     * authentication process and use <code>getRequirements()</code> and
222     * <code>submitRequirements()</code> to pass the credentials
223     * needed for authentication by the plugin authentication modules.
224     * The method <code>getStatus()</code> returns the
225     * authentication status.
226     *
227     * @param orgName name of the user's organization
228     * @param url URL of the OpenAm instance to talk to
229     * @throws AuthLoginException if <code>AuthContext</code> creation fails.
230     *         This exception is kept for backward compatibility only.
231     *
232     * @supported.api
233     */
234    public AuthContext(String orgName, URL url) throws AuthLoginException {
235        organizationName = orgName;
236        authURL = url;
237    }
238    
239    /**
240     * Constructs an instance of <code>AuthContext</code> for a given
241     * organization name, or sub organization name and a nick name
242     * for the certificate to be used in SSL handshake if client authentication
243     * is turn on in the server side.
244     * This organization or sub-organization name must be either "/" separated
245     * ( where it starts with "/" ) , DN , Domain name or DNS Alias Name.
246     *
247     * This constructor would be mainly used for the Certificate based
248     * authentication. If the certificate database contains multiple matching
249     * certificates for SSL, this constructor must be called in order for the
250     * desired certificate to be used for the Certificate based authentication.
251     *
252     * Caller would then use <code>login</code> to start the
253     * authentication process and use <code>getRequirements()</code> and
254     * <code>submitRequirements()</code> to pass the credentials
255     * needed for authentication by the plugin authentication modules.
256     * The method <code>getStatus()</code> returns the
257     * authentication status.
258     *
259     * @param orgName name of the user's organization
260     * @param nickName nick name for the certificate to be used
261     * @throws AuthLoginException if <code>AuthContext</code> creation fails.
262     *         This exception is kept for backward compatibility only.
263     *
264     * @supported.api
265     */
266    public AuthContext(String orgName, String nickName)
267            throws AuthLoginException {
268        organizationName = orgName;
269        this.nickName = nickName;
270    }
271    
272    /**
273     * Constructs an instance of <code>AuthContext</code> for a given
274     * organization name, or sub organization name, a nick name
275     * for the certificate to be used in SSL handshake if client authentication
276     * is enabled on the server side and the OpenAM URL.
277     * This organization or sub-organization name must be either "/" separated
278     * ( where it starts with "/" ) ,  DN , Domain name or a DNS Alias Name.
279     * And the <code>url</code> should specify the OpenAM server's protocol,
280     * host name, and port number,
281     * for example : <code>http://daye.red.iplanet.com:58080</code>
282     * This constructor would be mainly used for the Certificate based
283     * authentication. If the certificate database contains multiple matching
284     * certificates for SSL, this constructor must be called in order for the
285     * desired certificate to be used for the Certificate based authentication.
286     *
287     * Caller would then use <code>login</code> to start the
288     * authentication process and use <code>getRequirements()</code> and
289     * <code>submitRequirements()</code> to pass the credentials
290     * needed for authentication by the plugin authentication modules.
291     * The method <code>getStatus()</code> returns the
292     * authentication status.
293     *
294     * @param orgName name of the user's organization
295     * @param nickName nick name for the certificate to be used
296     * @param url URL of the OpenAM server to talk to
297     * @throws AuthLoginException if <code>AuthContext</code> creation fails.
298     *         This exception is kept for backward compatibility only.
299     *
300     * @supported.api
301     */
302    public AuthContext(String orgName, String nickName, URL url)
303            throws AuthLoginException {
304        organizationName = orgName;
305        this.nickName = nickName;
306        authURL = url;
307    }
308    
309    /**
310     * Constructs an instance of <code>AuthContext</code> for a given
311     * organization name, or sub organization name contained in the
312     * single sign on token.
313     *
314     * This constructor should be called for re-authentication of an
315     * authenticated user. single sign on token is the authenticated resource's
316     * Single-Sign-On Token. If the session properties based on
317     * the login method used matches those in the user's new
318     * authenticated  session then session upgrade will be done.
319     * A new session containing properties from both old single sign on token
320     * and new session shall be returned and old session will be
321     * destroyed if authentication  passes.
322     *
323     * Caller would then use <code>login</code> to start the
324     * authentication process and use <code>getRequirements()</code> and
325     * <code>submitRequirements()</code> to pass the credentials
326     * needed for authentication by the plugin authentication modules.
327     * The method <code>getStatus()</code> returns the
328     * authentication status.
329     *
330     * @param ssoToken single sign on token representing the resource's previous
331     *        authenticated session.
332     * @throws AuthLoginException if <code>AuthContext</code> creation fails.
333     *         This exception is kept for backward compatibility only.
334     *
335     * @supported.api
336     */
337    public AuthContext(SSOToken ssoToken) throws AuthLoginException {
338        this.ssoToken = ssoToken;
339    }
340    
341    /**
342     * Constructs an instance of <code>AuthContext</code> for a given
343     * organization name, or sub organization name contained in the
344     * single sign on token.
345     *
346     * This constructor should be called for re-authentication of an
347     * authenticated user. single sign on token is the authenticated resource's
348     * Single-Sign-On Token. If the session properties based on
349     * the login method used matches those in the user's new
350     * authenticated  session then session upgrade will be done.
351     * If forceAuth flag is <code>true</code> then the existing session 
352     * is used and no new session is created otherwise this constructor 
353     * behaves same as the constructor with no forceAuth flag.
354     *
355     * Caller would then use <code>login</code> to start the
356     * authentication process and use <code>getRequirements()</code> and
357     * <code>submitRequirements()</code> to pass the credentials
358     * needed for authentication by the plugin authentication modules.
359     * The method <code>getStatus()</code> returns the
360     * authentication status.
361     *
362     * @param ssoToken single sign on token representing the resource's 
363     *        previous authenticated session.
364     * @param forceAuth indicates that authentication preocess has to be 
365     *        restarted and given single sign on token will be used and new 
366     *        session will not be created.
367     * @throws AuthLoginException if <code>AuthContext</code> creation fails.
368     *         This exception is kept for backward compatibility only.
369     *
370     * @supported.api
371     */
372    public AuthContext(SSOToken ssoToken, boolean forceAuth) throws 
373        AuthLoginException {
374        this.ssoToken = ssoToken;
375        this.forceAuth = forceAuth;
376    }
377    
378    /**
379     * Starts the login process for the given <code>AuthContext</code> object.
380     *
381     * @exception AuthLoginException if an error occurred during login.
382     *
383     * @supported.api
384     */
385    public void login() throws AuthLoginException {
386        login(null, null, null, null, null, null);
387    }
388
389    /**
390     * Starts the login process for the given <code>AuthContext</code> object.
391     *
392     * @param request The HttpServletRequest that was sent to start the authentication process.
393     * @param response The corresponding HttpServletResponse for the HttpServletRequest.
394     * @throws AuthLoginException If an error occurred during login.
395     *
396     * @supported.api
397     */
398    public void login(HttpServletRequest request, HttpServletResponse response) throws AuthLoginException {
399        login(null, null, null, null, request, response);
400    }
401    
402    /**
403     * Starts the login process for the given <code>AuthContext</code> object
404     * identified by the index type and index name. The <code>IndexType</code>
405     * defines the possible kinds of "objects" or "resources" for which an
406     * authentication can be performed. Currently supported index types are
407     * users, roles, services (or application), levels, resources and
408     * mechanism/authentication modules.
409     *
410     * @param type Authentication index type.
411     * @param indexName Authentication index name.
412     * @exception AuthLoginException if an error occurred during login.
413     *
414     * @supported.api
415     */
416    public void login(IndexType type, String indexName)
417            throws AuthLoginException {
418        login(type, indexName, null, null, null, null);
419    }
420    
421    /**
422     * Starts the login process for the given <code>AuthContext</code> object
423     * identified by the index type and index name.
424     * The <code>IndexType</code> defines the possible kinds of "objects"
425     * or "resources" for which an authentication can
426     * be performed. Currently supported index types are
427     * users, roles, services (or application), levels, resources and mechanism.
428     * It allows the caller to pass in the desired locale for this request.
429     *
430     * @param type authentication index type
431     * @param indexName authentication index name
432     * @param locale locale setting
433     *
434     * @exception AuthLoginException if an error occurred during login
435     */
436    public void login(IndexType type, String indexName, String locale)
437            throws AuthLoginException {
438        login(type, indexName, null, null, locale);
439    }
440
441    /**
442     * Starts the login process for the given <code>AuthContext</code> object
443     * identified by the index type and index name and also completes
444     * the login process by submitting the given User credentials
445     * in the form of Callbacks.
446     * The <code>IndexType</code> defines the possible kinds of "objects"
447     * or "resources" for which an authentication can
448     * be performed. Currently supported index types are
449     * users, roles, services (or application), levels, resources and mechanism.
450     * <p>
451     * NOTE : This is a simplified wrapper method to eliminate multi-step calls
452     * to 'login' and submit credentials. This method is useful and will work
453     * only for those authentication modules which require only one set of
454     * callbacks or one page. This method can not be used to authenticate to
455     * authentication modules which require user interaction or multiple pages.
456     *
457     * @param type Authentication index type.
458     * @param indexName Authentication index name.
459     * @param userInfo User information/credentials in the form of array of
460     *        <code>Callback</code> objects. The <code>Callback</code> objects
461     *        array must be in the same order as defined in the authentication
462     *        module properties file, otherwise authentication module code will
463     *        not work.
464     * @return single-sign-on token for the valid user after successful
465     *         authentication.
466     * @exception AuthLoginException if an error occurred during login.
467     */
468    public SSOToken login(IndexType type, String indexName, Callback[] userInfo)
469            throws AuthLoginException {
470        login(type, indexName, null, null, null, null);
471        
472        SSOToken ssoToken = null;
473        Callback[] callbacks = null;
474        
475        while (hasMoreRequirements()) {
476            callbacks = getRequirements();
477            
478            if (callbacks != null) {
479                try {
480                    submitRequirements(userInfo);
481                } catch (Exception e) {
482                    if (authDebug.messageEnabled()) {
483                        authDebug.message(
484                            "Error: submitRequirements with userInfo : "
485                        + e.getMessage());
486                    }
487                    throw new AuthLoginException(e);
488                }
489            }
490        }
491        try {
492            if (getStatus() == AuthContext.Status.SUCCESS) {
493                ssoToken = getSSOToken();
494            }
495        } catch (Exception e) {
496            if (authDebug.messageEnabled()) {
497                authDebug.message("Error: getSSOToken : " + e.getMessage());
498            }
499            throw new AuthLoginException(e);
500        }
501        return ssoToken;
502    }
503    
504    /**
505     * Starts the login process for the given <code>AuthContext</code> object
506     * identified by the index type and index name with default parameters.
507     * The <code>IndexType</code> defines the possible kinds of "objects"
508     * or "resources" for which an authentication can be performed. Currently
509     * supported index types are users, roles, services (or application),
510     * levels, resources and mechanism/authentication modules.
511     *
512     * @param indexType authentication index type.
513     * @param indexName authentication index name.
514     * @param params contains the default values for the callbacks. The order
515     *        of this array matches the callbacks order for this login process.
516     *        value for the <code>PasswordCallback</code> is also in String
517     *        format, it will be converted to <code>char[]</code> when it is
518     *        set to the callback. Internal processing for this string array
519     *        uses <code>|</code> as separator. Hence <code>|</code> should not
520     *        be used in these default values. Currently only
521     *        <code>NameCallback</code> and <code>PasswordCallback</code> are
522     *        supported.
523     * @exception AuthLoginException if an error occurred during login.
524     *
525     * @supported.api
526     */
527    public void login(IndexType indexType, String indexName, String[] params)
528            throws AuthLoginException {
529        login(indexType, indexName, params, null, null, null);
530    }
531
532    public void login(IndexType indexType,
533                      String indexName,
534                      String[] params,
535                      HttpServletRequest request,
536                      HttpServletResponse response)
537            throws AuthLoginException {
538        login(indexType, indexName, params, null, request, response);
539    }
540    
541    /**
542     * Starts the login process for the given <code>AuthContext</code> object
543     * identified by the index type and index name with certain parameters
544     * and environment map.
545     * The <code>IndexType</code> defines the possible kinds of "objects"
546     * or "resources" for which an authentication can be performed. Currently
547     * supported index types are users, roles, services (or application),
548     * levels, modules and resources.
549     *
550     * @param indexType authentication index type.
551     * @param indexName authentication index name.
552     * @param params contains the default values for the callbacks. The order
553     *        of this array matches the callbacks order for this login process.
554     *        value for the <code>PasswordCallback</code> is also in String
555     *        format, it will be converted to <code>char[]</code> when it is
556     *        set to the callback. Internal processing for this string array
557     *        uses <code>|</code> as separator. Hence <code>|</code> should not
558     *        be used in these default values. Currently only
559     *        <code>NameCallback</code> and <code>PasswordCallback</code> are
560     *        supported.
561     * @param envMap contains the environment key/value pairs. Key is a String
562     *        object indicating the property name, value is a Set of String
563     *        values for the property. Currenty this parameter only applicable
564     *        when the indexTye is <code>AuthContext.IndexType.RESOURCE</code>.
565     * @exception AuthLoginException if an error occurred during login.
566     *
567     * @supported.api
568     */
569    public void login(IndexType indexType, String indexName, 
570        String[] params, Map envMap)
571            throws AuthLoginException {
572        login(indexType, indexName, params, envMap, null, null);
573    }
574    
575    public void login(
576        IndexType indexType,
577        String indexName,
578        String[] params,
579        Map envMap,
580        HttpServletRequest request,
581        HttpServletResponse response
582    ) throws AuthLoginException {
583        if (clientLocale == null) {
584            login(indexType, indexName, params, envMap, null, request, response);
585        } else {
586            String localeStr = clientLocale.toString();
587            login(indexType, indexName, params, envMap, localeStr, request, response);
588        }
589    }
590
591    private void login(
592        IndexType indexType,
593        String indexName,
594        String[] params,
595        Map envMap,
596        String locale
597    ) throws AuthLoginException {
598        login(indexType, indexName, params, envMap, locale, null, null);
599    }
600
601    private void login(
602        IndexType indexType,
603        String indexName,
604        String[] params,
605        Map envMap,
606        String locale,
607        HttpServletRequest request,
608        HttpServletResponse response
609    ) throws AuthLoginException {
610        if (ssoToken != null) {
611            try {
612                organizationName = ssoToken.getProperty(
613                    ISAuthConstants.ORGANIZATION);
614                ssoTokenID = ssoToken.getTokenID().toString();
615                authURL = sessionCache.getSession(new SessionID(ssoTokenID)).getSessionServiceURL();
616            } catch (Exception e) {
617                throw new AuthLoginException(e);
618            }
619        }
620        
621        if (authURL != null) {
622            authServiceURL = getAuthServiceURL(authURL.getProtocol(),
623                authURL.getHost(), Integer.toString(authURL.getPort()),
624                SystemProperties.get(Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR));
625        }
626        
627        AuthLoginException authException = null;
628        try {
629            if (authServiceURL == null) {
630                authServiceURL = getAuthServiceURL( server_proto,
631                    server_host, server_port, server_uri);
632            }
633            if (authServiceURL != null) {
634                if (authDebug.messageEnabled()) {
635                    authDebug.message("AuthContext.login : runLogin against "
636                            + authServiceURL);
637                }
638                runLogin(indexType, indexName, params, envMap, locale,
639                        request, response);
640                return;
641            }
642        } catch (AuthLoginException e) {
643            authException = e;
644            authDebug.error("Failed to login to " + authServiceURL);
645        } catch (Exception e) {
646            authDebug.error("Failed to login to " + authServiceURL
647                + ": " + e.getMessage(),e);
648        }
649        
650        if (authURL == null) {
651            // failover when authURL is not specified
652            Vector serviceURLs = null;
653            try {
654                serviceURLs = WebtopNaming.getServiceAllURLs(
655                AuthXMLTags.AUTH_SERVICE);
656            } catch (Exception e) {
657                throw new AuthLoginException(amAuthContext, "loginError",
658                new Object[]{e.getMessage()});
659            }
660            
661            if (authDebug.messageEnabled()) {
662                authDebug.message("Org Name : " + organizationName);
663                authDebug.message("ssoTokenID: " + ssoTokenID);
664                authDebug.message("serviceURLs: " + serviceURLs);
665            }
666            
667            if (serviceURLs != null) {
668                serviceURLs.remove(authServiceURL);
669                for (Enumeration e = serviceURLs.elements();
670                e.hasMoreElements(); ) {
671                    authServiceURL = (URL)e.nextElement();
672                    try {
673                        runLogin(indexType, indexName, params,
674                            envMap, locale, request, response);
675                        return;
676                    } catch (AuthLoginException ex) {
677                        authException = ex;
678                        authDebug.error("Failed to login in failover with " +
679                        authServiceURL + ": " + ex.getMessage());
680                    }
681                }
682            }
683        }
684        authDebug.error("Authentication failed.");
685        if (authException != null) {
686            throw authException;
687        } else {
688            throw new AuthLoginException(amAuthContext, "loginError",null);
689        }
690    }
691    
692    
693    private void runLogin(
694        IndexType indexType,
695        String indexName,
696        String[] params,
697        Map envMap,
698        String locale,
699        HttpServletRequest request,
700        HttpServletResponse response
701    ) throws AuthLoginException {
702        if (!localFlag) {
703            setLocalFlag(authServiceURL);
704        }
705
706        if (appSSOToken == null) {
707            if (!((indexType == IndexType.MODULE_INSTANCE) && 
708                (indexName.equals("Application")))){
709                appSSOToken = getAppSSOToken(false);
710            }
711        }
712        
713        if (localFlag) {
714            try {
715                if (ssoTokenID == null) {
716                    acLocal = com.sun.identity.authentication.service.AuthUtils.
717                        getAuthContext(organizationName);
718                } else {
719                    if (authDebug.messageEnabled()) {
720                        authDebug.message("AuthContext.runLogin: "
721                        + "ForceAuth = "+forceAuth);
722                    }
723                    acLocal = com.sun.identity.authentication.service.AuthUtils.
724                        getAuthContext(organizationName, ssoTokenID, false, 
725                            null, null, null, forceAuth);
726                }
727                LoginState loginState = acLocal.getLoginState();
728                /*
729                 * Set both the HttpRequest and HttpResponse on the login state so they are accessible by the Auth
730                 * Modules.
731                 */
732                if (request != null) {
733                    loginState.setHttpServletRequest(request);
734                    Hashtable hashtable = AuthClientUtils.parseRequestParameters(request);
735                    loginState.setParamHash(hashtable);
736                }
737                if (response != null) {
738                    loginState.setHttpServletResponse(response);
739                }
740                if (hostName != null) {
741                    acLocal.getLoginState().setClient(hostName);
742                }
743                acLocal.login(indexType, indexName, envMap, locale);
744            } catch (AuthException e) {
745                throw new AuthLoginException(e);
746            }
747            if (acLocal.getStatus().equals(Status.SUCCESS)) {
748                onSuccessLocal();
749            }
750            return;
751        }
752        
753        // Check if 7.0 RR stype protocol needs to be used
754        // This will setup NewAuthContext and authHandles
755        if (useOldStyleRemoteAuthentication) {
756            runRemoteOldAuthContext();
757            if (loginException != null) {
758                throw loginException;
759            }
760        }
761        // Run Login
762        runRemoteLogin(indexType, indexName, params, envMap, locale,
763                request, response);
764        // reset the retry count
765        retryRunLogin = DEFAULT_RETRY_COUNT;
766        
767        if (authDebug.messageEnabled()) {
768            authDebug.message("useNewStyleRemoteAuthentication : " 
769                + useNewStyleRemoteAuthentication);
770            authDebug.message("useOldStyleRemoteAuthentication : " 
771                + useOldStyleRemoteAuthentication);
772            authDebug.message("receivedDocument : " + receivedDocument);
773            authDebug.message("loginException : " + loginException);
774        }
775
776        // If "Login" fails and we have not set 6.3, 7.0 RR style protocol
777        // the server could be either 6.3 or 7.0 RR. Hence try "NewAuthContext"
778        // and then "Login"
779        if (!useNewStyleRemoteAuthentication &&
780            !useOldStyleRemoteAuthentication &&
781            (receivedDocument == null || 
782            (getAuthenticationHandle(receivedDocument)).equals("null")) && 
783            loginException != null) {
784            if (authDebug.messageEnabled()) {
785                authDebug.message("AuthContext: trying 6.3 style remote " +
786                    "AuthN and setting the flag to use 6.3 style");
787            }
788            useOldStyleRemoteAuthentication = true;
789            // Server could be either 6.3 or 7.0 RR, try old style
790            // Construct the Request XML with New AuthContext parameters
791            loginException = null;  // Reset loginException
792            runRemoteOldAuthContext();
793            if (loginException != null) {
794                throw loginException;
795            }
796            // Re-try login process with AuthIdentifier
797            runRemoteLogin(indexType, indexName, params,
798                envMap, locale, request, response);
799            // reset the retry count
800            retryRunLogin = DEFAULT_RETRY_COUNT;
801        } else if (!useNewStyleRemoteAuthentication) {
802            useNewStyleRemoteAuthentication = true;
803        }
804        if (loginException != null) {
805            throw loginException;
806        }
807    }
808
809    private void runRemoteLogin(IndexType indexType, String indexName, String[] params, Map envMap,
810            String locale, HttpServletRequest req, HttpServletResponse res) throws AuthLoginException {
811        try {
812            String xmlString;
813            // remote auth
814            StringBuilder request = new StringBuilder(100);
815            String authHandle = getAuthHandle();
816            if (ssoTokenID != null && "0".equals(authHandle)) {
817                if (authDebug.messageEnabled()) {
818                    authDebug.message("AuthContext.runRemoteLogin: Found SSOTokenID " + ssoTokenID);
819                }
820                authHandle = ssoTokenID;
821            }
822
823            request.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX, authHandle));
824            if (appSSOToken != null) {
825                request.append(AuthXMLTags.APPSSOTOKEN_BEGIN);
826                request.append(appSSOToken.getTokenID().toString());
827                request.append(AuthXMLTags.APPSSOTOKEN_END);
828            }
829            request.append(AuthXMLTags.LOGIN_BEGIN);
830
831            if (!useOldStyleRemoteAuthentication) {
832                request.append(AuthXMLTags.SPACE)
833                    .append(AuthXMLTags.ORG_NAME_ATTR)
834                    .append(AuthXMLTags.EQUAL)
835                    .append(AuthXMLTags.QUOTE)
836                    .append(XMLUtils.escapeSpecialCharacters(organizationName))
837                    .append(AuthXMLTags.QUOTE);
838                if (hostName != null) {
839                    request.append(AuthXMLTags.SPACE)
840                    .append(AuthXMLTags.HOST_NAME_ATTR)
841                    .append(AuthXMLTags.EQUAL)
842                    .append(AuthXMLTags.QUOTE)
843                    .append(XMLUtils.escapeSpecialCharacters(hostName))
844                    .append(AuthXMLTags.QUOTE);
845                }
846                if (locale != null && !locale.isEmpty()) {
847                        request.append(AuthXMLTags.SPACE)
848                        .append(AuthXMLTags.LOCALE)
849                        .append(AuthXMLTags.EQUAL)
850                        .append(AuthXMLTags.QUOTE)
851                        .append(XMLUtils.escapeSpecialCharacters(locale))
852                        .append(AuthXMLTags.QUOTE);
853                }
854                if (forceAuth) {
855                    request.append(AuthXMLTags.SPACE)
856                    .append(AuthXMLTags.FORCE_AUTH_ATTR)
857                    .append(AuthXMLTags.EQUAL)
858                    .append(AuthXMLTags.QUOTE)
859                    .append("true")
860                    .append(AuthXMLTags.QUOTE);
861                }
862            }
863            request.append(AuthXMLTags.ELEMENT_END);
864
865            if (indexType != null) {
866                request.append(AuthXMLTags.INDEX_TYPE_PAIR_BEGIN)
867                    .append(AuthXMLTags.SPACE)
868                    .append(AuthXMLTags.INDEX_TYPE)
869                    .append(AuthXMLTags.EQUAL)
870                    .append(AuthXMLTags.QUOTE);
871
872                if (indexType == IndexType.USER) {
873                    request.append(AuthXMLTags.INDEX_TYPE_USER_ATTR);
874                } else if (indexType == IndexType.ROLE) {
875                    request.append(AuthXMLTags.INDEX_TYPE_ROLE_ATTR);
876                } else if (indexType == IndexType.SERVICE) {
877                    request.append(AuthXMLTags.INDEX_TYPE_SVC_ATTR);
878                } else if (indexType == IndexType.MODULE_INSTANCE) {
879                    request.append(AuthXMLTags.INDEX_TYPE_MODULE_ATTR);
880                } else if (indexType == IndexType.LEVEL) {
881                    request.append(AuthXMLTags.INDEX_TYPE_LEVEL_ATTR);
882                } else if (indexType == IndexType.COMPOSITE_ADVICE) {
883                    request.append(AuthXMLTags.INDEX_TYPE_COMPOSITE_ADVICE_ATTR);
884                } else if (indexType == IndexType.RESOURCE) {
885                    request.append(AuthXMLTags.INDEX_TYPE_RESOURCE);
886                }
887                request.append(AuthXMLTags.QUOTE)
888                    .append(AuthXMLTags.ELEMENT_END)
889                    .append(AuthXMLTags.INDEX_NAME_BEGIN)
890                    .append(XMLUtils.escapeSpecialCharacters(indexName))
891                    .append(AuthXMLTags.INDEX_NAME_END)
892                    .append(AuthXMLTags.INDEX_TYPE_PAIR_END);
893            }
894
895            if (locale != null && locale.length() > 0) {
896                request.append(AuthXMLTags.LOCALE_BEGIN);
897                request.append(XMLUtils.escapeSpecialCharacters(locale));
898                request.append(AuthXMLTags.LOCALE_END);
899            }
900
901            if (params != null) {
902                StringBuilder paramString = new StringBuilder();
903                for (int i = 0; i < params.length; i++) {
904                    if (i != 0 ) {
905                        paramString.append(ISAuthConstants.PIPE_SEPARATOR);
906                    }
907                    paramString.append(XMLUtils.escapeSpecialCharacters(params[i]));
908                }
909                request.append(AuthXMLTags.PARAMS_BEGIN)
910                    .append(paramString.toString())
911                    .append(AuthXMLTags.PARAMS_END);
912            }
913            if (envMap != null && !envMap.isEmpty()) {
914                StringBuilder envString = new StringBuilder();
915                for (Map.Entry<String, Set<String>> entry : ((Map<String, Set<String>>) envMap).entrySet()) {
916                    // convert Map to XMLString as follows:
917                    // <EnvValue>keyname|value1|value2|...</EnvValue>
918                    String keyName = entry.getKey();
919                    Set<String> values = entry.getValue();
920                    if (values != null && !values.isEmpty()) {
921                        envString.append(AuthXMLTags.ENV_AV_BEGIN)
922                                .append(AuthClientUtils.escapePipe(XMLUtils.escapeSpecialCharacters(keyName)));
923                        for (String value : values) {
924                            envString.append(ISAuthConstants.PIPE_SEPARATOR)
925                                    .append(AuthClientUtils.escapePipe(XMLUtils.escapeSpecialCharacters(value)));
926                        }
927                        envString.append(AuthXMLTags.ENV_AV_END);
928                    }
929                }
930                request.append(AuthXMLTags.ENV_BEGIN)
931                    .append(envString.toString())
932                    .append(AuthXMLTags.ENV_END);
933            }
934            request.append(AuthXMLTags.LOGIN_END);
935
936            if (includeReqRes) {
937                request.append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_START)
938                .append(AuthXMLTags.HTTP_SERVLET_REQUEST_START);
939                String encObj = "";
940
941                if (req != null) {
942                    try {
943                        encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletRequest(req));
944                    } catch (IOException ioe) {
945                        authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http request", ioe);
946                    }
947
948                    if (authDebug.messageEnabled()) {
949                        authDebug.message("req=" + new RemoteHttpServletRequest(req).toString());
950                    }
951
952                    request.append(encObj);
953                }
954
955                request.append(AuthXMLTags.HTTP_SERVLET_REQUEST_END);
956                request.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_START);
957
958                if (res != null) {
959                    encObj = "";
960
961                    try {
962                        encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletResponse(res));
963                    } catch (IOException ioe) {
964                        authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http response", ioe);
965                    }
966
967                    if (authDebug.messageEnabled()) {
968                        authDebug.message("res=" + res);
969                    }
970
971                    request.append(encObj);
972                }
973
974                request.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_END)
975                .append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_END);
976            } else {
977                if (authDebug.messageEnabled()) {
978                    authDebug.message("Not including req/res " + includeReqRes);
979                }
980            }
981
982            request.append(AuthXMLTags.XML_REQUEST_SUFFIX);
983            xmlString = request.toString();
984
985            // process the request, which will check for exceptions
986            // and also get the authentication handle ID
987            receivedDocument = processRequest(xmlString);
988
989            // Check set the login status
990            checkAndSetLoginStatus();
991
992            // if the app token was refreshed, retry remote login
993            if (loginException != null &&
994                loginException.getErrorCode().equals(AMAuthErrorCode.REMOTE_AUTH_INVALID_SSO_TOKEN) &&
995                retryRunLogin > 0) {
996                retryRunLogin--;
997
998                if (authDebug.messageEnabled()) {
999                    authDebug.message("Run remote login failed due to expired app token, retying");
1000                }
1001
1002                // reset as we are starting again
1003                loginStatus = Status.IN_PROGRESS;
1004                runRemoteLogin(indexType, indexName, params, envMap, locale, req,  res);
1005            }
1006        } catch (AuthLoginException le) {
1007            // Login has failed
1008            loginStatus = Status.FAILED;
1009            loginException = le;
1010        }
1011    }
1012
1013    private void runRemoteOldAuthContext() throws AuthLoginException {
1014        try {
1015            StringBuilder request = new StringBuilder(100);
1016            String[] objs = { "0" };
1017            if (ssoTokenID != null) {
1018                objs[0] = ssoTokenID;
1019            }
1020            request.append(MessageFormat.format(
1021                AuthXMLTags.XML_REQUEST_PREFIX, (Object[])objs))
1022                .append(AuthXMLTags.NEW_AUTHCONTEXT_BEGIN)
1023                .append(AuthXMLTags.SPACE)
1024                .append(AuthXMLTags.ORG_NAME_ATTR)
1025                .append(AuthXMLTags.EQUAL)
1026                .append(AuthXMLTags.QUOTE)
1027                .append(XMLUtils.escapeSpecialCharacters(organizationName))
1028                .append(AuthXMLTags.QUOTE)
1029                .append(AuthXMLTags.ELEMENT_END)
1030                .append(AuthXMLTags.NEW_AUTHCONTEXT_END)
1031                .append(AuthXMLTags.XML_REQUEST_SUFFIX);
1032            // process the request, which will check for exceptions
1033            // and also get the authentication handle ID
1034            receivedDocument = processRequest(request.toString());
1035
1036            // Check set the login status
1037            checkAndSetLoginStatus();
1038        } catch (AuthLoginException le) {
1039            // Login has failed
1040            loginStatus = Status.FAILED;
1041            loginException = le;
1042        }
1043    }
1044    
1045    /**
1046     * Returns the set of Principals or Subject the user has been
1047     * authenticated as.
1048     * This should be invoked only after successful authentication.
1049     *
1050     * @return <code>Subject</code> for the authenticated User.
1051     *         If the authentication fails or the authentication is in process,
1052     *         this will return <code>null</code>.
1053     *
1054     * @supported.api
1055     */
1056    public Subject getSubject() {
1057        if (localFlag) {
1058            if (!acLocal.getStatus().equals(Status.SUCCESS)) {
1059                return (null);
1060            }
1061            return (acLocal.getSubject());
1062        } else {
1063            if (!loginStatus.equals(Status.SUCCESS)) {
1064                return (null);
1065            }
1066            return (getSubject(receivedDocument));
1067        }
1068    }
1069
1070   /**
1071    * Returns a <code>Map</code> object that
1072    * that contains cookies set by AM server
1073    *
1074    * @return a <code>Map</code> of cookie name and
1075    * <code>Cookie</code> object.
1076    */
1077    public Map getCookieTable() {
1078        return cookieTable;
1079    }
1080    
1081    /**
1082     * Returns <code>true</code> if the login process requires more
1083     * information from the user to complete the authentication.
1084     * <p>
1085     * NOTE: This method has to be called as a condition of a
1086     * <code>while</code> loop in order to complete the authentication process
1087     * and get the correct <code>Status</code> after submitting the
1088     * requirements.
1089     *
1090     * @return <code>true</code> if more credentials are required from the user.
1091     *
1092     * @supported.api
1093     */
1094    public boolean hasMoreRequirements() {
1095        if (localFlag) {
1096            return (acLocal.hasMoreRequirements(false));
1097        } else {
1098            if ((!loginStatus.equals(Status.IN_PROGRESS)) ||
1099            ((getCallbacks(receivedDocument, false)) == null)) {
1100                return (false);
1101            }
1102            return (true);
1103        }
1104    }
1105    
1106    /**
1107     * Returns <code>true</code> if the login process requires more information
1108     * from the user to complete the authentication.
1109     *
1110     * NOTE: This method has to be called as a condition of a <ode>while</code>
1111     * loop in order to complete the authentication process and get the correct
1112     * <code>Status</code> after submitting the requirements.
1113     *
1114     * @param noFilter flag indicates whether to filter
1115     *        <code>PagePropertiesCallback</code> or not. Value
1116     *        <code>true</code> will not filter
1117     *        <code>PagePropertiesCallback</code>.
1118     * @return <code>true</code> if more credentials are required from the user.
1119     *
1120     * @supported.api
1121     */
1122    public boolean hasMoreRequirements(boolean noFilter) {
1123        if (localFlag) {
1124            return (acLocal.hasMoreRequirements(noFilter));
1125        } else {
1126            if ((!loginStatus.equals(Status.IN_PROGRESS)) ||
1127            ((getCallbacks(receivedDocument, noFilter)) == null)) {
1128                return (false);
1129            }
1130            return (true);
1131        }
1132    }
1133    
1134    /**
1135     * Returns an array of <code>Callback</code> objects that must be populated
1136     * by the user and returned back. These objects are requested by the
1137     * authentication plug-ins, and these are usually displayed to the user.
1138     * The user then provides the requested information for it to be
1139     * authenticated.
1140     *
1141     * @return an array of <code>Callback</code> objects requesting credentials
1142     *         from user
1143     *
1144     * @supported.api
1145     */
1146    public Callback[] getRequirements() {
1147        if (localFlag) {
1148            if (!acLocal.getStatus().equals(Status.IN_PROGRESS)) {
1149                return (null);
1150            }
1151            return (acLocal.getRequirements(false));
1152        } else {
1153            if (!loginStatus.equals(Status.IN_PROGRESS)) {
1154                return (null);
1155            }
1156            return (getCallbacks(receivedDocument, false));
1157        }
1158    }
1159    
1160    /**
1161     * Returns an array of <code>Callback</code> objects that
1162     * must be populated by the user and returned back.
1163     * These objects are requested by the authentication plug-ins,
1164     * and these are usually displayed to the user. The user then provides
1165     * the requested information for it to be authenticated.
1166     *
1167     * @param noFilter boolean flag indicating whether to filter
1168     * <code>PagePropertiesCallback</code> or not. Value <code>true</code> will
1169     * not filter <code>PagePropertiesCallback</code>.
1170     *
1171     * @return an array of <code>Callback</code> objects requesting credentials
1172     * from user
1173     *
1174     * @supported.api
1175     */
1176    public Callback[] getRequirements(boolean noFilter) {
1177        if (localFlag) {
1178            if (!acLocal.getStatus().equals(Status.IN_PROGRESS)) {
1179                return (null);
1180            }
1181            return (acLocal.getRequirements(noFilter));
1182        } else {
1183            if (!loginStatus.equals(Status.IN_PROGRESS)) {
1184                return (null);
1185            }
1186            return (getCallbacks(receivedDocument, noFilter));
1187        }
1188    }
1189
1190    /**
1191     * Fetches the remote request from the context
1192     *
1193     * @return The Http Servlet Request
1194     */
1195    public HttpServletRequest getRemoteRequest() {
1196        return remoteRequest;
1197    }
1198
1199    /**
1200     * Fetches the remote response from the context
1201     *
1202     * @return The Http Servlet Response
1203     */
1204    public HttpServletResponse getRemoteResponse() {
1205        return remoteResponse;
1206    }
1207    
1208    /**
1209     * Submits the populated <code>Callback</code> objects to the
1210     * authentication plug-in modules. Called after <code>getRequirements</code>
1211     * method and obtaining user's response to these requests.
1212     *
1213     * @param info Array of <code>Callback</code> objects.
1214     *
1215     * @supported.api
1216     */
1217    public void submitRequirements(Callback[] info) {
1218        submitRequirements(info, null, null);
1219    }
1220
1221    public void submitRequirements(Callback[] info, HttpServletRequest request,
1222            HttpServletResponse response) {
1223        if (authDebug.messageEnabled()) {
1224            authDebug.message("submitRequirements with Callbacks : " + info);
1225        }
1226        
1227        if (localFlag) {
1228            // Check if we are still in login session
1229            if (!acLocal.getStatus().equals(Status.IN_PROGRESS)) {
1230                return;
1231            }
1232            acLocal.submitRequirements(info);
1233            if (acLocal.getStatus().equals(Status.SUCCESS)) {
1234                onSuccessLocal();
1235            }
1236            return;
1237        } else {
1238            // Check if we are still in login session
1239            if (!loginStatus.equals(Status.IN_PROGRESS)) {
1240                return;
1241            }
1242            
1243            // Construct the XML
1244            try {
1245                StringBuilder xml = new StringBuilder(100);
1246                String[] authHandles = new String[1];
1247                authHandles[0] = getAuthenticationHandle(receivedDocument);
1248                xml.append(MessageFormat.format(
1249                    AuthXMLTags.XML_REQUEST_PREFIX,(Object[])authHandles));
1250                if (appSSOToken != null) {
1251                    xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN);
1252                    xml.append(appSSOToken.getTokenID().toString()).
1253                        append(AuthXMLTags.APPSSOTOKEN_END);
1254                }
1255                xml.append(AuthXMLTags.SUBMIT_REQS_BEGIN)
1256                .append(AuthXMLUtils.getXMLForCallbacks(info));
1257
1258                if (clientLocale != null) {
1259                    String localeStr = clientLocale.toString();
1260                    if ((localeStr != null) && (localeStr.length() > 0)) {
1261                        xml.append(AuthXMLTags.LOCALE_BEGIN)
1262                        .append(XMLUtils.escapeSpecialCharacters(localeStr))
1263                        .append(AuthXMLTags.LOCALE_END);
1264                    }
1265                }
1266
1267                xml.append(AuthXMLTags.SUBMIT_REQS_END);
1268
1269                if (includeReqRes) {
1270                    // serialized request and response objects
1271                    xml.append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_START)
1272                    .append(AuthXMLTags.HTTP_SERVLET_REQUEST_START);
1273                    String encObj = "";
1274
1275                    if (request != null) {
1276                        try {
1277                            encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletRequest(request));
1278                        } catch (IOException ioe) {
1279                            authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http request", ioe);
1280                        }
1281
1282                        if (authDebug.messageEnabled()) {
1283                            authDebug.message("req=" + request);
1284                        }
1285
1286                        xml.append(encObj);
1287                    }
1288
1289                    xml.append(AuthXMLTags.HTTP_SERVLET_REQUEST_END);
1290                    xml.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_START);
1291
1292                    if (response != null) {
1293                        encObj = "";
1294
1295                        try {
1296                            encObj = AuthXMLUtils.serializeToString(new RemoteHttpServletResponse(response));
1297                        } catch (IOException ioe) {
1298                            authDebug.error("AuthXMLUtils::runRemoteLogin Unable to serailize http response", ioe);
1299                        }
1300
1301                        if (authDebug.messageEnabled()) {
1302                            authDebug.message("res=" + response);
1303                        }
1304
1305                        xml.append(encObj);
1306                    }
1307
1308                    xml.append(AuthXMLTags.HTTP_SERVLET_RESPONSE_END)
1309                    .append(AuthXMLTags.REMOTE_REQUEST_RESPONSE_END);
1310                }
1311                xml.append(AuthXMLTags.XML_REQUEST_SUFFIX);
1312                
1313                // Send the request to be processes
1314                receivedDocument = processRequest(xml.toString());
1315                
1316                // Check set the login status
1317                checkAndSetLoginStatus();
1318            } catch (AuthLoginException le) {
1319                // Login has failed
1320                loginStatus = Status.FAILED;
1321                loginException = le;
1322            }
1323        }
1324    }
1325    
1326    /**
1327     * Logs out the user and also invalidates the single sign on token
1328     * associated with this <code>AuthContext</code>.
1329     *
1330     * @throws AuthLoginException if an error occurred during logout.
1331     *
1332     * @supported.api
1333     */
1334    public void logout() throws AuthLoginException {
1335        if (localFlag) {
1336            acLocal.logout();
1337            return;
1338        }
1339
1340        // Construct the XML
1341        try {
1342            StringBuilder xml = new StringBuilder(100);
1343            String[] authHandles = new String[1];
1344            authHandles[0] = getAuthenticationHandle(receivedDocument);
1345            xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX,
1346            (Object[])authHandles));
1347            if (appSSOToken != null) {
1348                xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN);
1349                xml.append(appSSOToken.getTokenID().toString()).
1350                    append(AuthXMLTags.APPSSOTOKEN_END);
1351            }
1352            xml.append(AuthXMLTags.LOGOUT_BEGIN)
1353               .append(AuthXMLTags.LOGOUT_END)
1354               .append(AuthXMLTags.XML_REQUEST_SUFFIX);
1355            
1356            // Send the request to be processes
1357            receivedDocument = processRequest(xml.toString());
1358            
1359            // Check set the login status
1360            checkAndSetLoginStatus();
1361        } catch (AuthLoginException le) {
1362            // Login has failed
1363            loginStatus = Status.FAILED;
1364            loginException = le;
1365        }
1366    }
1367
1368    /**
1369     * Logs out the user and also invalidates the single sign on token
1370     * associated with this <code>AuthContext</code>.
1371         *
1372         * This method causes the logout to happen on the server and the 
1373         * correct SPI hooks to be called.
1374     *
1375     * @throws AuthLoginException if an error occurred during logout.
1376     *
1377     * @supported.api
1378     */
1379    public void logoutUsingTokenID()
1380    throws AuthLoginException {
1381        if (localFlag) {
1382            return;
1383        }
1384
1385        if (ssoToken != null) {
1386            try {
1387                organizationName = ssoToken.getProperty(
1388                    ISAuthConstants.ORGANIZATION);
1389                ssoTokenID = ssoToken.getTokenID().toString();
1390                authURL = sessionCache.getSession(new SessionID(ssoTokenID)).getSessionServiceURL();
1391            } catch (Exception e) {
1392                throw new AuthLoginException(e);
1393            }
1394        }
1395
1396        if (authURL != null) {
1397            authServiceURL = getAuthServiceURL(authURL.getProtocol(),
1398                authURL.getHost(), Integer.toString(authURL.getPort()),
1399                authURL.getPath());
1400        }
1401
1402
1403        // Construct the XML
1404        try {
1405            StringBuilder xml = new StringBuilder(100);
1406            String[] authHandles = new String[1];
1407            authHandles[0] = ssoToken.getTokenID().toString();
1408            xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX,
1409            (Object[]) authHandles));
1410            if (appSSOToken != null) {
1411                xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN);
1412                xml.append(appSSOToken.getTokenID().toString()).
1413                    append(AuthXMLTags.APPSSOTOKEN_END);
1414            }
1415            xml.append(AuthXMLTags.LOGOUT_BEGIN)
1416            .append(AuthXMLTags.LOGOUT_END)
1417            .append(AuthXMLTags.XML_REQUEST_SUFFIX);
1418
1419            // Send the request to be processes
1420            receivedDocument = processRequest(xml.toString());
1421
1422            // Check set the login status
1423            checkAndSetLoginStatus();
1424        } catch (AuthLoginException le) {
1425            // Login has failed
1426            loginStatus = Status.FAILED;
1427            loginException = le;
1428        }
1429    }
1430    
1431    /**
1432     * Returns login exception, if any, during the authentication process.
1433     * Typically set when the login fails.
1434     *
1435     * @return login exception.
1436     * @supported.api
1437     */
1438    public AuthLoginException getLoginException() {
1439        if (localFlag) {
1440            return (acLocal.getLoginException());
1441        } else {
1442            return (loginException);
1443        }
1444    }
1445    
1446    /**
1447     * Returns the Single-Sign-On (SSO) Token for the authenticated
1448     * user. If the user has not successfully authenticated
1449     * <code>Exception</code> will be thrown.
1450     * <p>
1451     * Single sign token can be used as the authenticated token.
1452     *
1453     * @return Single-Sign-On token for the valid user after successful
1454     *         authentication.
1455     * @throws L10NMessageImpl if the user is not authenticated or an error is
1456     *         encountered in retrieving the user's single sign on token.
1457     * @supported.api
1458     */
1459    public SSOToken getSSOToken() throws L10NMessageImpl {
1460        if (localFlag) {
1461            if (!acLocal.getStatus().equals(Status.SUCCESS)) {
1462                throw new L10NMessageImpl(
1463                    amAuthContext, "statusNotSuccess", null);
1464            }
1465            return (acLocal.getSSOToken());
1466        } else {
1467            // Get the loginStatus node
1468            if (!loginStatus.equals(Status.SUCCESS)) {
1469                throw new L10NMessageImpl(
1470                    amAuthContext, "statusNotSuccess", null);
1471            }
1472            Node loginStatusNode = XMLUtils.getRootNode(receivedDocument,
1473            AuthXMLTags.LOGIN_STATUS);
1474            if (loginStatusNode == null) {
1475                throw new L10NMessageImpl(amAuthContext, "noStatusNode", null);
1476            }
1477            
1478            String ssoTokenIDTmp = XMLUtils.getNodeAttributeValue(loginStatusNode,
1479                AuthXMLTags.SSOTOKEN);
1480            try {
1481                return new com.iplanet.sso.providers.dpro.SSOProviderImpl().
1482                    createSSOToken(ssoTokenIDTmp, true);
1483            } catch (SSOException ssoe) {
1484                throw new L10NMessageImpl(
1485                    amAuthContext, "createSSOTokenError", null);
1486            }
1487        }
1488    }
1489    
1490    /**
1491     * Returns the current status of the authentication process as
1492     * <code>AuthContext.Status</code>.
1493     *
1494     * @return <code>Status</code> of the authentication process.
1495     *
1496     * @supported.api
1497     */
1498    public Status getStatus() {
1499        if (localFlag) {
1500            return (acLocal.getStatus());
1501        } else {
1502            return (loginStatus);
1503        }
1504    }
1505    
1506    /**
1507     * Returns the current Auth Identifier of the authentication
1508     * process as String Session ID.
1509     *
1510     * @return Auth Identifier of the authentication process.
1511     */
1512    public String getAuthIdentifier() {
1513        if (localFlag) {
1514            return (acLocal.getAuthIdentifier());
1515        } else {
1516            return (getAuthHandle());
1517        }
1518    }
1519    
1520    /**
1521     * Returns the Successful Login URL for the authenticated user.
1522     *
1523     * @return the Successful Login URL for the authenticated user.
1524     * @throws Exception if it fails to get url for auth success
1525     */
1526    public String getSuccessURL() throws Exception {
1527        if (localFlag) {
1528            if (!acLocal.getStatus().equals(Status.SUCCESS)) {
1529                throw new
1530                L10NMessageImpl(amAuthContext, "statusNotSuccess", null);
1531            }
1532            return (acLocal.getSuccessURL());
1533        } else {
1534            // Get the loginStatus node
1535            if (!loginStatus.equals(Status.SUCCESS)) {
1536                throw new
1537                L10NMessageImpl(amAuthContext, "statusNotSuccess", null);
1538            }
1539            Node loginStatusNode = XMLUtils.getRootNode(receivedDocument,
1540            AuthXMLTags.LOGIN_STATUS);
1541            if (loginStatusNode == null) {
1542                throw new L10NMessageImpl(amAuthContext, "noStatusNode", null);
1543            }
1544            return (XMLUtils.getNodeAttributeValue(loginStatusNode,
1545            AuthXMLTags.SUCCESS_URL));
1546        }
1547    }
1548    
1549    /**
1550     * Returns the Failure Login URL for the authenticating user.
1551     *
1552     * @return the Failure Login URL for the authenticating user
1553     * @throws Exception if it fails to get url for auth failure
1554     */
1555    public String getFailureURL() throws Exception {
1556        if (localFlag) {
1557            return (acLocal.getFailureURL());
1558        } else {
1559            // Get the loginStatus node
1560            Node loginStatusNode = XMLUtils.getRootNode(receivedDocument,
1561            AuthXMLTags.LOGIN_STATUS);
1562            if (loginStatusNode == null) {
1563                throw new L10NMessageImpl(amAuthContext, "noStatusNode", null);
1564            }
1565            return (XMLUtils.getNodeAttributeValue(loginStatusNode,
1566            AuthXMLTags.FAILURE_URL));
1567        }
1568    }
1569    
1570    /**
1571     * Resets this instance of <code>AuthContext</code> object, so that a new
1572     * login process can be initiated. A new authentication process can started
1573     * using any one of the <code>login</code> methods.
1574     */
1575    public void reset() {
1576        loginStatus = Status.NOT_STARTED;
1577        //organizationName = null;
1578        //receivedDocument = null;
1579        //loginException = null;
1580    }
1581    
1582    /**
1583     * Returns the the organization name that was set during the
1584     * <code>AuthContext</code> constructor.
1585     *
1586     * @return Organization name in the <code>AuthContext</code>.
1587     *
1588     * @supported.api
1589     */
1590    public String getOrganizationName() {
1591        return (this.organizationName);
1592    }
1593    
1594    /**
1595     *
1596     * Returns authentication module/s instances (or plugins) configured
1597     * for a organization, or sub-organization name that was set during the
1598     * <code>AuthContext</code> constructor.
1599     *
1600     * @return Set of Module instance names.
1601     *
1602     * @supported.api
1603     */
1604    public Set getModuleInstanceNames() {
1605        if (authURL != null) {
1606            authServiceURL = getAuthServiceURL(
1607                authURL.getProtocol(),
1608                authURL.getHost(), 
1609                Integer.toString(authURL.getPort()),
1610                authURL.getPath());
1611        }
1612        if (!localFlag) {
1613            setLocalFlag(authServiceURL);
1614        }
1615        if (localFlag) {
1616            return (acLocal.getModuleInstanceNames());
1617        } else {
1618            if (authServiceURL == null) {
1619                try {
1620                    authServiceURL = getAuthServiceURL(server_proto,
1621                        server_host, server_port, server_uri);
1622                } catch (Exception e) {
1623                    return Collections.EMPTY_SET;
1624                }
1625            }
1626            sendQueryInformation(AuthXMLTags.MODULE_INSTANCE);
1627            
1628            //Receive data
1629            Node queryResultNode = XMLUtils.getRootNode(receivedDocument,
1630            AuthXMLTags.QUERY_RESULT);
1631            if (queryResultNode == null) {
1632                return (null);
1633            }
1634            
1635            // Iteratate through moduleInstanceNames
1636            HashSet moduleInstanceNames = new HashSet();
1637            NodeList childNodes = queryResultNode.getChildNodes();
1638            if ( childNodes != null ) {
1639                for (int i = 0; i < childNodes.getLength(); i++) {
1640                    Node childNode = childNodes.item(i);
1641                    String moduleName = XMLUtils.getValueOfValueNode(childNode);
1642                    moduleInstanceNames.add(moduleName);
1643                }
1644            }
1645            return (moduleInstanceNames);
1646        }
1647    }
1648    
1649    /**
1650     * Terminates an ongoing <code>login</code> call that has not yet completed.
1651     *
1652     * @exception AuthLoginException if an error occurred during abort.
1653     *
1654     * @supported.api
1655     */
1656    public void abort() throws AuthLoginException {
1657        if (localFlag) {
1658            acLocal.abort();
1659            return;
1660        }
1661        
1662        // Construct the XML
1663        try {
1664            StringBuilder xml = new StringBuilder(100);
1665            String[] authHandles = new String[1];
1666            authHandles[0] = getAuthenticationHandle(receivedDocument);
1667            xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX,
1668            (Object[])authHandles));
1669            if (appSSOToken != null) {
1670                xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN);
1671                xml.append(appSSOToken.getTokenID().toString()).
1672                    append(AuthXMLTags.APPSSOTOKEN_END);
1673            }
1674            xml.append(AuthXMLTags.ABORT_BEGIN)
1675            .append(AuthXMLTags.ABORT_END)
1676            .append(AuthXMLTags.XML_REQUEST_SUFFIX);
1677            
1678            // Send the request to be processes
1679            receivedDocument = processRequest(xml.toString());
1680            
1681            // Check set the login status
1682            checkAndSetLoginStatus();
1683        } catch (AuthLoginException le) {
1684            // Login has failed
1685            loginStatus = Status.FAILED;
1686            loginException = le;
1687        }
1688    }
1689    
1690    /**
1691     * Sets the password for the certificate database.
1692     * It is required to call only once to initialize certificate database if
1693     * the password is not set in the password file (specified as
1694     * the value for <code>com.iplanet.am.admin.cli.certdb.passfile</code>
1695     * in <code>AMConfig.properties</code>). If both are set, this method will
1696     * overwrite the value in certificate password file.
1697     *
1698     * @param password Password for the certificate database.
1699     *
1700     * @supported.api
1701     */
1702    public static void setCertDBPassword(String password) {
1703        try {
1704            if (usingJSSEHandler) {
1705                Class pcbClass = (Class) Class.forName(JSSE_PASSWORD_CALLBACK);
1706                Object passwdCallback = (Object) pcbClass.newInstance();
1707                Method method =
1708                pcbClass.getMethod("setPassword", new Class[] { String.class });
1709                KeyStore keystore = (KeyStore)method.invoke(
1710                    passwdCallback, new Object[] { password });
1711            } else {
1712                Class initializer = Class.forName(JSS_PASSWORD_UTIL);
1713                Constructor initializerConstructor = initializer.getConstructor(
1714                    new Class[] { String.class });
1715                initializerConstructor.newInstance(new Object[] { password });
1716            }
1717        } catch (Exception e) {
1718            e.printStackTrace();
1719            authDebug.message("Error in setCertDBPassword : " + e.getMessage());
1720        }
1721    }
1722    
1723    
1724    /**
1725     * Returns the error template.
1726     *
1727     * @return error template.
1728     */
1729    public String getErrorTemplate() {
1730        if (localFlag) {
1731            return (acLocal.getErrorTemplate());
1732        } else {
1733            if (receivedDocument == null) {
1734                //something went terribly wrong, let's return with internal error template
1735                return AuthClientUtils.getErrorTemplate(AMAuthErrorCode.AUTH_ERROR);
1736            }
1737            String errTemplate = "";
1738            Node exceptionNode = XMLUtils.getRootNode(receivedDocument,
1739            AuthXMLTags.EXCEPTION);
1740            if (exceptionNode != null) {
1741                errTemplate = XMLUtils.getNodeAttributeValue(exceptionNode,
1742                AuthXMLTags.TEMPLATE_NAME);
1743            }
1744            return errTemplate;
1745        }
1746    }
1747    
1748    /**
1749     * Returns the error message.
1750     *
1751     * @return error message.
1752     */
1753    public String getErrorMessage() {
1754        if (localFlag) {
1755            return (acLocal.getErrorMessage());
1756        } else {
1757            if (receivedDocument == null) {
1758                //something went terribly wrong, let's return with internal error message
1759                return AuthClientUtils.getErrorMessage(AMAuthErrorCode.AUTH_ERROR);
1760            }
1761            String errMessage = null;
1762            Node exceptionNode = XMLUtils.getRootNode(receivedDocument,
1763            AuthXMLTags.EXCEPTION);
1764            if (exceptionNode != null) {
1765                errMessage = XMLUtils.getNodeAttributeValue(exceptionNode,
1766                AuthXMLTags.MESSAGE);
1767            }
1768            return errMessage;
1769        }
1770    }
1771    
1772    /**
1773     * Returns error code.
1774     *
1775     * @return error code with white space trimmed
1776     */
1777    public String getErrorCode() {
1778        if (localFlag) {
1779            return (acLocal.getErrorCode());
1780        } else {
1781            if (receivedDocument == null) {
1782                //something went terribly wrong
1783                return AMAuthErrorCode.AUTH_ERROR;
1784            }
1785            String errCode = "";
1786            Node exceptionNode = XMLUtils.getRootNode(receivedDocument,
1787            AuthXMLTags.EXCEPTION);
1788
1789            if (exceptionNode != null) {
1790                errCode = XMLUtils.getNodeAttributeValue(exceptionNode,
1791                AuthXMLTags.ERROR_CODE);
1792            }
1793
1794            if (errCode != null) {
1795                return errCode.trim();
1796            } else {
1797                return errCode;
1798            }
1799        }
1800    }
1801    
1802    /**
1803     * Sets the client's hostname or IP address.This could be used
1804     * by the policy component to restrict access to resources.
1805     * This method is ineffective if the "Remote Auth Security" option under 
1806     * the global configuration of Core Authentication Service is not enabled.
1807     * This method must be called before calling <code>login</code> method.
1808     * If it is called after calling <code>login</code> then 
1809     * it is ineffective.
1810     *
1811     * @param hostname hostname or ip address
1812     *
1813     * @supported.api
1814     */
1815    public void setClientHostName(String hostname) {
1816        this.hostName = hostname;
1817    }
1818
1819    /**
1820     * Returns the client's hostname or IP address as set by 
1821     * setClientHostName
1822     * 
1823     * @return hostname/IP address
1824     *
1825     * @supported.api
1826     */
1827    public String getClientHostName() {
1828        return (hostName);
1829    }
1830
1831    /**
1832     * Sets locale based on user locale preferemce.
1833     *
1834     * @param loc locale preference of user
1835     */
1836    public void setLocale (java.util.Locale loc) {
1837        clientLocale = loc;
1838    }
1839
1840    /**
1841     * Returns locale preference set in AuthConext
1842     * @return - user prefered locale.
1843     */
1844
1845    public java.util.Locale getLocale () {
1846        return clientLocale;
1847    }
1848    
1849    private AuthLoginException checkException(){
1850        AuthLoginException exception = null;
1851        String error = getErrorCode();
1852
1853        // if the app token is invalid, refresh the token
1854        if (error != null && error.equals(AMAuthErrorCode.REMOTE_AUTH_INVALID_SSO_TOKEN)) {
1855            appSSOToken = getAppSSOToken(true);
1856        }
1857
1858        if (error != null && error.length() != 0){
1859            exception = new AuthLoginException("amAuth", error, null);
1860        } else {
1861            error = getErrorMessage();
1862            if (error != null && error.length() != 0) {
1863                exception = new AuthLoginException(error);
1864            }
1865        }
1866        return exception;
1867    }
1868    
1869    protected void checkAndSetLoginStatus(){
1870        
1871        Node loginStatusNode = XMLUtils.getRootNode(
1872        receivedDocument, AuthXMLTags.LOGIN_STATUS);
1873        if (loginStatusNode == null) {
1874            loginException = checkException();
1875            
1876            if (includeReqRes) {
1877                remoteRequest = AuthXMLUtils.getRemoteRequest(
1878                    XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE));
1879                remoteResponse = AuthXMLUtils.getRemoteResponse(
1880                    XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE));
1881            }
1882        } else {
1883            //since there was no error, we should reset the loginException, to handle the case when the first auth
1884            //server was not available.
1885            loginException = null;
1886            // Get the status attribute
1887            String status = XMLUtils.getNodeAttributeValue(
1888            loginStatusNode, AuthXMLTags.STATUS);
1889            if (status != null) {
1890                if (status.equals(Status.SUCCESS.toString())) {
1891                    loginStatus = Status.SUCCESS;
1892                } else if (status.equals(Status.FAILED.toString())) {
1893                    loginStatus = Status.FAILED;
1894                    loginException = checkException();
1895                } else if (status.equals(Status.COMPLETED.toString())) {
1896                    loginStatus = Status.COMPLETED;
1897                } else if (status.equals(Status.IN_PROGRESS.toString())) {
1898                    loginStatus = Status.IN_PROGRESS;
1899                } else if (status.equals(Status.RESET.toString())) {
1900                    loginStatus = Status.RESET;
1901                }
1902            }
1903
1904            if (includeReqRes) {
1905                remoteRequest = AuthXMLUtils.getRemoteRequest(
1906                    XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE));
1907                remoteResponse = AuthXMLUtils.getRemoteResponse(
1908                    XMLUtils.getRootNode(receivedDocument, AuthXMLTags.REMOTE_REQUEST_RESPONSE));
1909            }
1910
1911            if (authDebug.messageEnabled()) {
1912                authDebug.message("LoginStatus : " + loginStatus);
1913            }
1914        }
1915    }
1916    
1917    protected void sendQueryInformation(String reqInfo) {
1918        // Construct the XML
1919        try {
1920            StringBuilder xml = new StringBuilder(100);
1921            String[] authHandles = new String[1];
1922            authHandles[0] = getAuthHandle();
1923            
1924            xml.append(MessageFormat.format(AuthXMLTags.XML_REQUEST_PREFIX,
1925            (Object[])authHandles));
1926            if (appSSOToken != null) {
1927                xml.append(AuthXMLTags.APPSSOTOKEN_BEGIN);
1928                xml.append(appSSOToken.getTokenID().toString()).
1929                    append(AuthXMLTags.APPSSOTOKEN_END);
1930            }
1931            xml.append(AuthXMLTags.QUERY_INFO_BEGIN)
1932               .append(AuthXMLTags.SPACE)
1933               .append(AuthXMLTags.REQUESTED_INFO)
1934               .append(AuthXMLTags.EQUAL)
1935               .append(AuthXMLTags.QUOTE)
1936               .append(reqInfo)
1937               .append(AuthXMLTags.QUOTE);
1938
1939            if (authHandles[0].equals("0")) {
1940                xml.append(AuthXMLTags.SPACE)
1941                    .append(AuthXMLTags.ORG_NAME_ATTR)
1942                    .append(AuthXMLTags.EQUAL)
1943                    .append(AuthXMLTags.QUOTE)
1944                    .append(XMLUtils.escapeSpecialCharacters(organizationName))
1945                    .append(AuthXMLTags.QUOTE);
1946            }
1947
1948            xml.append(AuthXMLTags.ELEMENT_END)
1949                .append(AuthXMLTags.QUERY_INFO_END)
1950                .append(AuthXMLTags.XML_REQUEST_SUFFIX);
1951            
1952            // Send the request to be processes
1953            receivedDocument = processRequest(xml.toString());
1954            
1955            // Check set the login status
1956            checkAndSetLoginStatus();
1957        } catch (AuthLoginException le) {
1958            // Login has failed
1959            loginStatus = Status.FAILED;
1960            loginException = le;
1961        }
1962    }
1963    
1964    private void setLocalFlag(URL url) {
1965        try {
1966            String urlStr = url.getProtocol() + "://" + url.getHost() + ":"
1967                + Integer.toString(url.getPort());
1968            
1969            if (authDebug.messageEnabled()) {
1970                authDebug.message("in setLocalFlag(), url : " + urlStr);
1971                authDebug.message("AuthContext.localAuthServiceID : " +
1972                    localAuthServiceID);
1973            }
1974            
1975            if ((localAuthServiceID != null) &&
1976                (urlStr.equalsIgnoreCase(localAuthServiceID))
1977            ) {
1978                localFlag = true;
1979            }
1980        } catch (Exception e) {
1981            authDebug.error("AuthContext::setLocalFlag:: " + e);
1982        }
1983    }
1984    
1985    protected Document processRequest(String xmlRequest)
1986            throws AuthLoginException {
1987        Document doc = null;
1988
1989        try {
1990            Request request = new Request(xmlRequest);
1991            RequestSet set = new RequestSet(AuthXMLTags.AUTH_SERVICE);
1992            set.addRequest(request);
1993            
1994            URL url = authServiceURL;
1995            
1996            if (url.getProtocol().equals("https") && (nickName != null)) {
1997                Class[] paramtype = {String.class};
1998                Object[] param = {nickName};
1999                String protHandler = protHandlerPkg + ".https.Handler";
2000                Constructor construct =
2001                    Class.forName(protHandler).getConstructor(paramtype);
2002                URLStreamHandler handler =
2003                    (URLStreamHandler)construct.newInstance(param);
2004                url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
2005                url.getFile(), handler);
2006            }
2007            
2008            if (authDebug.messageEnabled()) {
2009                authDebug.message("Service URL : " + url.toString());
2010            }
2011
2012            Vector responses = PLLClient.send(url, set, cookieTable);
2013            
2014            if ((responses.isEmpty()) || (responses.size() != 1)) {
2015                throw new L10NMessageImpl(amAuthContext, "responseError", null);
2016            }
2017            
2018            Response res = (Response) responses.elementAt(0);
2019            String responseStr = (String)res.getContent();
2020            
2021            doc = XMLUtils.getXMLDocument(
2022                new ByteArrayInputStream(responseStr.getBytes("UTF-8")));
2023        } catch (Exception e) {
2024            authDebug.message("error in getting service url", e);
2025            throw new AuthLoginException(amAuthContext, "xmlProcessError",
2026                null, e);
2027        }
2028        return (doc);
2029    }
2030    
2031    protected static void checkForException(Document document)
2032            throws AuthLoginException {
2033        Node exceptionNode = XMLUtils.getRootNode(
2034            document, AuthXMLTags.EXCEPTION);
2035
2036        if (exceptionNode != null) {
2037            throw (new AuthLoginException(XMLUtils.getNodeAttributeValue(
2038                exceptionNode, AuthXMLTags.MESSAGE)));
2039        }
2040    }
2041    
2042    protected String getAuthenticationHandle(Document document)
2043            throws AuthLoginException {
2044        Node responseNode = XMLUtils.getRootNode(
2045            document, AuthXMLTags.RESPONSE);
2046        if (responseNode == null) {
2047            throw new AuthLoginException(amAuthContext, "responseError", null);
2048        }
2049        
2050        String authID = XMLUtils.getNodeAttributeValue(
2051            responseNode, AuthXMLTags.AUTH_ID_HANDLE);
2052        return (authID);
2053    }
2054    
2055    protected static Callback[] getCallbacks(
2056        Document document,
2057        boolean noFilter) {
2058        return (AuthXMLUtils.getCallbacks(XMLUtils.getRootNode(document,
2059            AuthXMLTags.CALLBACKS), noFilter));
2060    }
2061    
2062    protected static Subject getSubject(Document document) {
2063        Node loginStatusNode = XMLUtils.getRootNode(document,
2064            AuthXMLTags.LOGIN_STATUS);
2065
2066        if (loginStatusNode == null) {
2067            return (null);
2068        }
2069        
2070        Node subjectNode = XMLUtils.getChildNode(loginStatusNode,
2071        AuthXMLTags.SUBJECT);
2072        
2073        if (subjectNode == null) {
2074            return (null);
2075        }
2076        
2077        String subject = XMLUtils.getValueOfValueNode(subjectNode);
2078        try {
2079            Subject sSubject = AuthXMLUtils.getDeSerializedSubject(subject);
2080            
2081            if (authDebug.messageEnabled()) {
2082                authDebug.message("Deserialized subject : "
2083                    + sSubject.toString());
2084            }
2085            return sSubject;
2086        } catch (Exception e) {
2087            authDebug.message("get Deserialized subject error : " , e);
2088            return null;
2089        }
2090        
2091    }
2092    
2093    protected static String getXMLforSubject(Subject subject) {
2094        if (subject == null) {
2095            return ("");
2096        }
2097        StringBuilder request = new StringBuilder(100);
2098        request.append(AuthXMLTags.SUBJECT_BEGIN);
2099        String serializeSubject = AuthXMLUtils.getSerializedSubject(subject);
2100        request.append(serializeSubject);
2101        request.append(AuthXMLTags.SUBJECT_END);
2102        return (request.toString());
2103    }
2104    
2105    /**
2106     * Returns the account lockout message. This can be either a dynamic
2107     * message indicating the number of tries left or the the account
2108     * deactivated message.
2109     *
2110     * @return account lockout message.
2111     */
2112    public String getLockoutMsg() {
2113        String lockoutMsg = null;
2114        if (localFlag) {
2115            lockoutMsg = acLocal.getLockoutMsg();
2116        } else {
2117            // Account Lockout Warning Check by scanning the error
2118            // message in the exception thrown by the server
2119            lockoutMsg = getErrorMessage();
2120            if((lockoutMsg == null) ||
2121                (lockoutMsg.indexOf("Account lockout") == -1)){
2122                lockoutMsg = "";
2123            }
2124        }
2125        return lockoutMsg;
2126    }
2127    
2128    /**
2129     * Returns <code>true</code> if account is lock out.
2130     *
2131     * @return <code>true</code> if account is lock out.
2132     */
2133    public boolean isLockedOut() {
2134        boolean isLockedOut = false;
2135        if (localFlag) {
2136            isLockedOut = acLocal.isLockedOut();
2137        } else {
2138            // TBD
2139        }
2140        
2141        return isLockedOut;
2142    }
2143    
2144    /**
2145     * The class <code>Status</code> defines the possible
2146     * authentication states during the login process.
2147     *
2148     * @supported.all.api
2149     */
2150    public static class Status extends Object {
2151        
2152        private String status;
2153        
2154        /**
2155         * The <code>NOT_STARTED</code> status indicates that the login process
2156         * has not yet started. Basically, it means that the method
2157         * <code>login</code> has not been called.
2158         */
2159        public static final Status NOT_STARTED = new Status("not_started");
2160        
2161        /**
2162         * The <code>IN_PROGRESS</code> status indicates that the login process
2163         * is in progress. Basically, it means that the <code>login</code>
2164         * method has been called and that this object is waiting for the user
2165         * to send authentication information.
2166         */
2167        public static final Status IN_PROGRESS = new Status("in_progress");
2168        
2169        /**
2170         *
2171         * The <code>SUCCESS</code> indicates that the login process has
2172         * succeeded.
2173         */
2174        public static final Status SUCCESS = new Status("success");
2175        
2176        /**
2177         * The <code>FAILED</code> indicates that the login process has failed.
2178         */
2179        public static final Status FAILED = new Status("failed");
2180        
2181        /**
2182         *
2183         * The <code>COMPLETED</code> indicates that the user has been
2184         * successfully logged out.
2185         */
2186        public static final Status COMPLETED = new Status("completed");
2187        
2188        /**
2189         * The <code>RESET</code> indicates that the login process has been
2190         * reset or re-initialized.
2191         */
2192        public static final Status RESET = new Status("reset");
2193        
2194        /**
2195         * The <code>ORG_MISMATCH</code> indicates that the framework
2196         * <code>org</code> and the <code>org</code> required by the user do
2197         * not match.
2198         */
2199        public static final Status ORG_MISMATCH = new Status("org_mismatch");
2200        
2201        
2202        private Status() {
2203            // do nothing
2204        }
2205        
2206        private Status(String s) {
2207            status = s;
2208        }
2209        
2210        /**
2211         * Returns the string representation of the authentication status.
2212         *
2213         * @return String representation of authentication status.
2214         */
2215        public String toString() {
2216            return (status);
2217        }
2218        
2219        /**
2220         * Checks if two authentication status objects are equal.
2221         *
2222         * @param authStatus Reference object with which to compare.
2223         * @return <code>true</code> if the objects are same.
2224         */
2225        public boolean equals(Object authStatus) {
2226            if (authStatus instanceof Status) {
2227                Status s = (Status) authStatus;
2228                return (s.status.equalsIgnoreCase(status));
2229            }
2230            return (false);
2231        }
2232    }
2233    
2234    /**
2235     * The class <code>IndexType</code> defines the possible kinds of "objects"
2236     * or "resources" for which an authentication can be performed.
2237     *
2238     * @supported.all.api
2239     */
2240    public static class IndexType extends Object {
2241        
2242        private String index;
2243        
2244        /**
2245         * The <code>USER</code> index type indicates that the index name given
2246         * corresponds to a user.
2247         */
2248        public static final IndexType USER = new IndexType("user");
2249        
2250        /**
2251         * The <code>ROLE</code> index type indicates that the index name given
2252         * corresponds to a role.
2253         */
2254        public static final IndexType ROLE = new IndexType("role");
2255        
2256        /**
2257         *
2258         * The <code>SERVICE</code> index type indicates that the index name
2259         * given corresponds to a service (or application).
2260         */
2261        public static final IndexType SERVICE = new IndexType("service");
2262        
2263        /**
2264         * The <code>LEVEL</code> index type indicates that the index name
2265         * given corresponds to a given authentication level.
2266         */
2267        public static final IndexType LEVEL = new IndexType("level");
2268        
2269        /**
2270         * The <code>MODULE_INSTANCE</code> index type indicates that the index
2271         * name given corresponds to one of the authentication modules.
2272         */
2273        public static final IndexType MODULE_INSTANCE =
2274            new IndexType("module_instance");
2275        
2276        /**
2277         * The <code>RESOURCE</code> index type indicates that the index
2278         * name given corresponds to a given policy protected resource URL.
2279         */
2280        public static final IndexType RESOURCE =
2281            new IndexType("resource");
2282        
2283        /**
2284         * The <code>COMPOSITE_ADVICE</code> index type indicates that the
2285         * index name given corresponds to string in the form of XML
2286         * representing different Policy Authentication conditions, example
2287         * <code>AuthSchemeCondition</code>, <code>AuthLevelCondition</code>,
2288         * etc.
2289         */
2290        public static final IndexType COMPOSITE_ADVICE =
2291            new IndexType("composite_advice");
2292        
2293        private IndexType() {
2294            // do nothing
2295        }
2296        
2297        private IndexType(String s) {
2298            index = s;
2299        }
2300        
2301        /**
2302         * Returns the string representation of the index type.
2303         *
2304         * @return String representation of index type.
2305         */
2306        public String toString() {
2307            return (index);
2308        }
2309        
2310        /**
2311         * Checks if two index type objects are equal.
2312         *
2313         * @param indexType Reference object with which to compare.
2314         *
2315         * @return <code>true</code> if the objects are same.
2316         */
2317        public boolean equals(Object indexType) {
2318            if (indexType instanceof IndexType) {
2319                IndexType s = (IndexType) indexType;
2320                return (s.index.equalsIgnoreCase(index));
2321            }
2322            return (false);
2323        }
2324    }
2325    
2326    private String getAuthHandle() {
2327        String handle = null;
2328        
2329        if (receivedDocument != null) {
2330            try {
2331                handle = getAuthenticationHandle(receivedDocument);
2332            } catch (Exception e) {
2333                // do nothing
2334            }
2335        }
2336        if ( handle == null ) {
2337            handle = "0";
2338        }
2339        return handle;
2340    }
2341    
2342    private static URL getAuthServiceURL(
2343        String protocol,
2344        String host,
2345        String port,
2346        String uri
2347    ) {
2348        URL authservice = null;
2349        try {
2350            authservice = WebtopNaming.getServiceURL(AuthXMLTags.AUTH_SERVICE,
2351                protocol, host, port, uri);
2352        } catch (Exception e) {
2353            authDebug.error("Failed to obtain auth service url from server: " +
2354            protocol + "://" + host + ":" + port);
2355        }
2356        return authservice;
2357    }
2358    
2359    private void onSuccessLocal() {
2360        if (localSessionChecked) {
2361            return;
2362        }
2363        SSOToken currToken = acLocal.getSSOToken();
2364        com.iplanet.dpro.session.service.InternalSession oldSess
2365            = acLocal.getLoginState().getOldSession();
2366        if (oldSess != null) {
2367            if (forceAuth) {
2368                try {
2369                    SSOTokenManager.getInstance().
2370                        destroyToken(currToken);
2371                } catch (SSOException ssoExp) {
2372                    authDebug.error("AuthContext.onSuccessLocal: ",
2373                        ssoExp);
2374        
2375                }
2376                acLocal.getLoginState().setSession(oldSess);
2377                acLocal.getLoginState().setForceAuth(false);
2378                ssoToken = acLocal.getSSOToken();
2379                ssoTokenID = ssoToken.getTokenID().toString();
2380                
2381            } else {
2382                InjectorHolder.getInstance(SessionService.class).destroyInternalSession(oldSess.getID());
2383            }
2384        }
2385        localSessionChecked = true;
2386    }
2387
2388    /**
2389     * Returns the application sso token. Can perform a check to ensure that
2390     * the app token is still valid (requires a session refresh call to OpenAM)
2391     *
2392     * @param refresh true if we should check with OpenAM if the app token is valid
2393     * @return a valid application's sso token.
2394     */
2395    private SSOToken getAppSSOToken(boolean refresh) {
2396        SSOToken appToken = null;
2397
2398        try {
2399            appToken = (SSOToken) AccessController.doPrivileged(
2400                            AdminTokenAction.getInstance());
2401        } catch (AMSecurityPropertiesException aspe) {
2402            if (authDebug.messageEnabled()) {
2403                authDebug.message("AuthContext::getAppSSOToken: " +
2404                                  "unable to get app ssotoken " + aspe.getMessage());
2405            }
2406        }
2407
2408        if (refresh) {
2409            // ensure the token is valid
2410            try {
2411                SSOTokenManager ssoTokenManager = SSOTokenManager.getInstance();
2412                ssoTokenManager.refreshSession(appToken);
2413
2414                if (!ssoTokenManager.isValidToken(appToken)) {
2415                    if (authDebug.messageEnabled()) {
2416                        authDebug.message("AuthContext.getAppSSOToken(): " +
2417                                          "App SSOToken is invalid, retrying");
2418                    }
2419
2420                    try {
2421                        appToken = (SSOToken) AccessController.doPrivileged(
2422                                                AdminTokenAction.getInstance());
2423                    } catch (AMSecurityPropertiesException aspe) {
2424                        if (authDebug.messageEnabled()) {
2425                            authDebug.message("AuthContext::getAppSSOToken: " +
2426                                              "unable to get app ssotoken " + aspe.getMessage());
2427                        }
2428                    }
2429                }
2430            } catch (SSOException ssoe) {
2431                if (authDebug.messageEnabled()) {
2432                    authDebug.message("AuthContext.getAppSSOToken(): " +
2433                                      "unable to refresh app token: " + ssoe.getL10NMessage());
2434                }
2435
2436                try {
2437                    appToken = (SSOToken) AccessController.doPrivileged(
2438                                            AdminTokenAction.getInstance());
2439                } catch (AMSecurityPropertiesException aspe) {
2440                    if (authDebug.errorEnabled()) {
2441                        authDebug.error("AuthContext::getAppSSOToken: " +
2442                                          "unable to get app ssotoken " + aspe.getMessage());
2443                    }
2444                }
2445            }
2446        }
2447
2448        if (authDebug.messageEnabled()) {
2449            if (appToken == null) {
2450                authDebug.message("Null App SSO Token");
2451            } else {
2452                authDebug.message("Obtained App Token= " + appToken.getTokenID().toString());
2453            }
2454        }
2455
2456        return appToken;
2457    }
2458
2459    public AuthContextLocal getAuthContextLocal() {
2460        return acLocal;
2461    }
2462}