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