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: AMLoginModule.java,v 1.22 2009/11/21 01:11:56 222713 Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted 2010-2015 ForgeRock AS.
031 */
032
033package com.sun.identity.authentication.spi;
034
035import static org.forgerock.openam.audit.AuditConstants.EntriesInfoFieldKey.*;
036import static org.forgerock.openam.utils.StringUtils.*;
037
038import com.iplanet.am.sdk.AMException;
039import com.iplanet.am.sdk.AMUser;
040import com.iplanet.am.sdk.AMUserPasswordValidation;
041import com.iplanet.am.util.Misc;
042import com.iplanet.dpro.session.service.InternalSession;
043import com.iplanet.dpro.session.service.SessionConstraint;
044import com.iplanet.dpro.session.service.SessionCount;
045import com.iplanet.sso.SSOException;
046import com.iplanet.sso.SSOToken;
047import com.iplanet.sso.SSOTokenManager;
048import com.sun.identity.authentication.AuthContext;
049import com.sun.identity.authentication.audit.AuthenticationModuleEventAuditor;
050import com.sun.identity.authentication.callbacks.HiddenValueCallback;
051import com.sun.identity.authentication.callbacks.ScriptTextOutputCallback;
052import com.sun.identity.authentication.service.AMAuthErrorCode;
053import com.sun.identity.authentication.service.AuthD;
054import com.sun.identity.authentication.service.AuthException;
055import com.sun.identity.authentication.service.LoginState;
056import com.sun.identity.authentication.service.LoginStateCallback;
057import com.sun.identity.authentication.util.ISAuthConstants;
058import com.sun.identity.authentication.util.ISValidation;
059import com.sun.identity.common.AccountLockoutInfo;
060import com.sun.identity.common.AdministrationServiceListener;
061import com.sun.identity.common.DNUtils;
062import com.sun.identity.common.ISAccountLockout;
063import com.sun.identity.idm.AMIdentity;
064import com.sun.identity.idm.AMIdentityRepository;
065import com.sun.identity.idm.IdRepoException;
066import com.sun.identity.idm.IdType;
067import com.sun.identity.idm.IdUtils;
068import com.sun.identity.shared.Constants;
069import com.sun.identity.shared.datastruct.CollectionHelper;
070import com.sun.identity.shared.debug.Debug;
071import com.sun.identity.shared.locale.AMResourceBundleCache;
072import com.sun.identity.sm.OrganizationConfigManager;
073import com.sun.identity.sm.ServiceSchema;
074import com.sun.identity.sm.ServiceSchemaManager;
075import org.forgerock.guice.core.InjectorHolder;
076import org.forgerock.openam.audit.model.AuthenticationAuditEntry;
077import org.forgerock.openam.ldap.LDAPUtils;
078
079import javax.security.auth.Subject;
080import javax.security.auth.callback.Callback;
081import javax.security.auth.callback.CallbackHandler;
082import javax.security.auth.callback.ChoiceCallback;
083import javax.security.auth.callback.ConfirmationCallback;
084import javax.security.auth.callback.NameCallback;
085import javax.security.auth.callback.PasswordCallback;
086import javax.security.auth.callback.TextInputCallback;
087import javax.security.auth.callback.TextOutputCallback;
088import javax.security.auth.callback.UnsupportedCallbackException;
089import javax.security.auth.login.LoginException;
090import javax.security.auth.spi.LoginModule;
091import javax.servlet.http.HttpServletRequest;
092import javax.servlet.http.HttpServletResponse;
093import java.io.IOException;
094import java.security.Principal;
095import java.util.ArrayList;
096import java.util.Collections;
097import java.util.HashMap;
098import java.util.HashSet;
099import java.util.Iterator;
100import java.util.List;
101import java.util.Map;
102import java.util.ResourceBundle;
103import java.util.Set;
104
105/**
106 * An abstract class which implements JAAS LoginModule, it provides
107 * methods to access OpenAM services and the module
108 * xml configuration.
109 * <p>
110 * Because it is an abstract class, Login Module writers must subclass
111 * and implement init(), process(), getPrincipal() methods.
112 * <p>
113 * The Callback[] for the Login Module is dynamically generated based
114 * on the xml module configuration. The module configuration file name
115 * must be the same as the name of the class (no package name) and have the
116 * extension .xml.
117 * <p>
118 * Here is a sample module configuration file:
119 * <pre>
120 * &lt;ModuleProperties moduleClass="LDAP" version="1.0" &gt;
121 *     &lt;Callbacks length="2" order="1" timeout="60" header="LDAP
122 *     Authentication" &gt;
123 *         &lt;NameCallback&gt;
124 *             &lt;Prompt&gt; Enter UserId &lt;/Prompt&gt;
125 *         &lt;/NameCallback&gt;
126 *         &lt;PasswordCallback echoPassword="false" &gt;
127 *             &lt;Prompt&gt; Enter Password &lt;/Prompt&gt;
128 *         &lt;/PasswordCallback&gt;
129 *     &lt;/Callbacks&gt;
130 *     &lt;Callbacks length="3" order="2" timeout="120" header="Password
131 *     Expiring Please Change" &gt;
132 *         &lt;PasswordCallback echoPassword="false" &gt;
133 *             &lt;Prompt&gt; Enter Current Password &lt;/Prompt&gt;
134 *         &lt;/PasswordCallback&gt;
135 *         &lt;PasswordCallback echoPassword="false" &gt;
136 *             &lt;Prompt&gt; Enter New Password &lt;/Prompt&gt;
137 *         &lt;/PasswordCallback&gt;
138 *         &lt;PasswordCallback echoPassword="false" &gt;
139 *             &lt;Prompt&gt; Confirm New Password &lt;/Prompt&gt;
140 *         &lt;/PasswordCallback&gt;
141 *     &lt;/Callbacks&gt;
142 * &lt;/ModuleProperties&gt;
143 * </pre>
144 * Each Callbacks Element corresponds to one login state.
145 * When an authentication process is invoked, there will be Callback[]
146 * generated from user's Login Module for each state. All login state
147 * starts with 1, then module controls the login process, and decides what's
148 * the next state to go in the process() method.
149 * <p>
150 * In the sample module configuration shown above, state one has
151 * three Callbacks, Callback[0] is for module information, Callback[1] is
152 * for user ID, Callback[2] is for user password. When the user fills in the
153 * Callbacks, those Callback[] will be sent to the process() method, where
154 * the module writer gets the submitted Callbacks, validates them and returns.
155 * If user's password is expiring, the module writer will set the next
156 * state to 2. State two has four Callbacks to request user to change
157 * password. The process() routine is again
158 * called after user submits the Callback[]. If the module writer throws an
159 * LoginException, an 'authentication failed' page will be sent to the user.
160 * If no exception is thrown, the user will be redirected to their default
161 * page.
162 * <p>
163 * The optional 'timeout' attribute in each state is used to ensure that the
164 * user responds in a timely manner. If the time between sending the Callbacks
165 * and getting response is greater than the timeout, a timeout page will be
166 * sent.
167 * <p>
168 * There are also optional 'html' and 'image' attribute in each state. The
169 * 'html' attribute allows the module writer to use a custom HTML
170 * page for the Login UI. The 'image' attribute allows the writer to display
171 * a custom background image on each page.
172 * <p>
173 * When multiple states are available to the user, the Callback array from a
174 * previous state may be retrieved by using the <code>getCallbak(int)</code>
175 * methods. The underlying login module keeps the Callback[] from the previous
176 * states until the login process is completed.
177 * <p>
178 * If a module writer need to substitute dynamic text in next state, the writer
179 * could use the <code>getCallback()</code> method to get the Callback[] for the
180 * next state, modify the output text or prompt, then call
181 * <code>replaceCallback()</code> to update the Callback array. This allows a
182 * module writer to dynamically generate challenges, passwords or user IDs.
183 * <p>
184 * Each authentication session will create a new instance of your
185 * Login Module Java class. The reference to the class will be
186 * released once the authentication session has either succeeded
187 * or failed. It is important to note that any static data or
188 * reference to any static data in your Login module
189 * must be thread-safe.
190 * <p>
191 *
192 * For a complete sample, please refer to
193 * &lt;install_root&gt;/SUNWam/samples/authentication/providers
194 *
195 * @supported.api
196 */
197public abstract class AMLoginModule implements LoginModule {
198    // list which holds both presentation and credential callbacks
199    List internal = null;
200    // list which holds only credential callbacks
201    List external = null;
202    // list which contains the original Callback list from AMModuleProperties
203    List origList = null;
204    // class name
205    private String fileName = null;
206    // if true, means this module does not hava any Callbacks defined, this
207    // is the case for anonymous/cert, which have a size 0 config file
208    boolean noCallbacks = false;
209    
210    // constant for empty Callback array
211    private static Callback[] EMPTY_CALLBACK = new Callback[0];
212    
213    // state length for this module
214    private int stateLength = 0;
215    
216    // resource bundle
217    private ResourceBundle bundle = null;
218    
219    // login state
220    private LoginState loginState =null;
221    
222    /**
223     * Holds callback handler object passed in through initialize method
224     */
225    private CallbackHandler handler = null;
226    /**
227     * Holds subject object passed in through initialize method
228     */
229    private Subject subject = null;
230    /**
231     * Holds shared state map passed in through initialize method
232     */
233    private Map sharedState = null;
234    /**
235     * Holds options map passed in through initialize method
236     */
237    private Map options = null;
238    
239    private static Debug debug = Debug.getInstance("amLoginModule");
240    
241    private int currentState = ISAuthConstants.LOGIN_START;
242    
243    private final String EMPTY_STRING = "";
244    private String moduleName = null;
245    private String moduleClass = null;
246    private static final String bundleName = "amAuth";
247    private static AuthD ad = AuthD.getAuth();
248    private Principal principal = null;
249    // the authentication status
250    private boolean succeeded = false;
251
252    private boolean forceCallbacksRead = false;
253    
254    //use Shared state by default disabled
255    private boolean isSharedState = false;
256    private boolean isStore = true;
257    private String sharedStateBehaviorPattern = "";
258
259    // variable used in replaceHeader()
260    private String headerWithReplaceTag;
261    private boolean alreadyReplaced = false;
262    private int lastState = 0;
263        
264    /**
265     * Holds handle to ResourceBundleCache to quickly get ResourceBundle for
266     * any Locale.
267     */
268    protected static AMResourceBundleCache amCache =
269        AMResourceBundleCache.getInstance();
270
271    protected final AuthenticationModuleEventAuditor auditor;
272
273    /**
274     * No argument constructor for {@link AMLoginModule}.
275     */
276    public AMLoginModule() {
277        auditor = InjectorHolder.getInstance(AuthenticationModuleEventAuditor.class);
278    }
279    
280    /**
281     * Clone Callback[], and save it in the internal/external
282     * callbacks list. External callback contains all user defined
283     * Callbacks in the xml module configuration (property file),
284     * internal callback contains the external callbacks plus the
285     * PagePropertiesCallback. Note here, although
286     * Callback[] in internal/external are different, the Callback
287     * instance they pointed are actually same instance
288     * @param index indicates state of callback
289     * @param original original array of callback to be cloned
290     * @return Callback[] returns cloned callback
291     * @exception AuthLoginException if callback can not be cloned
292     */
293    private Callback[] cloneCallbacks(int index, Callback[] original)
294    throws AuthLoginException {
295        // check if there is any callbacks in original
296        if (original == null || original.length == 0) {
297            // this is the error case where there is no Callbacks
298            // defined for a state
299            debug.error("cloneCallbacks, no callbacks in state " + (index+1));
300            throw new AuthLoginException(bundleName, "noCallbackState",
301            new Object[]{new Integer(index + 1)});
302        }
303        
304        int len = original.length;
305        // Callback array which hold the cloned Callbacks
306        Callback[] copy = new Callback[len];
307        // List which contains the external callbacks only
308        List extCallbacks = new ArrayList();
309        
310        // iterate through Callback array, and copy them one by one
311        // if it is an external Callback, add to the extCallback list
312        for (int i = 0; i < len; i++) {
313            if (original[i] instanceof HiddenValueCallback) {
314                final HiddenValueCallback hiddenValueCallback = (HiddenValueCallback) original[i];
315                String defaultValue = hiddenValueCallback.getDefaultValue();
316                if (defaultValue != null && defaultValue.length() != 0) {
317                    copy[i] = new HiddenValueCallback(
318                            hiddenValueCallback.getId(), defaultValue);
319                } else {
320                    copy[i] = new HiddenValueCallback(
321                            hiddenValueCallback.getId());
322                }
323                extCallbacks.add(copy[i]);
324                if (debug.messageEnabled()) {
325                    debug.message("clone #" + i + " is HiddenValueCallback");
326                }
327            } else if (original[i] instanceof NameCallback) {
328                String dftName = ((NameCallback) original[i]).getDefaultName();
329                if (dftName != null && dftName.length() != 0) {
330                    copy[i] = new NameCallback(
331                            ((NameCallback) original[i]).getPrompt(), dftName);
332                } else {
333                    copy[i] = new NameCallback(
334                            ((NameCallback) original[i]).getPrompt());
335                }
336                extCallbacks.add(copy[i]);
337                if (debug.messageEnabled()) {
338                    debug.message("clone #" + i + " is NameCallback");
339                }
340            } else if (original[i] instanceof PasswordCallback) {
341                copy[i] = new PasswordCallback(
342                ((PasswordCallback) original[i]).getPrompt(),
343                ((PasswordCallback) original[i]).isEchoOn());
344                extCallbacks.add(copy[i]);
345                if (debug.messageEnabled()) {
346                    debug.message("clone #" + i + " is PasswordCallback");
347                }
348            } else if (original[i] instanceof ScriptTextOutputCallback) {
349                copy[i] = new ScriptTextOutputCallback(
350                        ((TextOutputCallback) original[i]).getMessage());
351                extCallbacks.add(copy[i]);
352                if (debug.messageEnabled()) {
353                    debug.message("clone #" + i + " is ScriptTextOutputCallback");
354                }
355            } else if (original[i] instanceof TextOutputCallback) {
356                copy[i] = new TextOutputCallback(
357                ((TextOutputCallback) original[i]).getMessageType(),
358                ((TextOutputCallback) original[i]).getMessage());
359                extCallbacks.add(copy[i]);
360                if (debug.messageEnabled()) {
361                    debug.message("clone #" + i + " is TextOutputCallback");
362                }
363            } else if (original[i] instanceof PagePropertiesCallback) {
364                // PagePropertiesCallback, no need to add to external callbacks
365                copy[i] = new PagePropertiesCallback(
366                ((PagePropertiesCallback) original[i]).getModuleName(),
367                ((PagePropertiesCallback) original[i]).getHeader(),
368                ((PagePropertiesCallback) original[i]).getImage(),
369                ((PagePropertiesCallback) original[i]).getTimeOutValue(),
370                ((PagePropertiesCallback) original[i]).getTemplateName(),
371                ((PagePropertiesCallback) original[i]).getErrorState(),
372                ((PagePropertiesCallback) original[i]).getPageState());
373                ((PagePropertiesCallback) copy[i]).setRequire(
374                ((PagePropertiesCallback) original[i]).getRequire());
375                ((PagePropertiesCallback) copy[i]).setAttribute(
376                ((PagePropertiesCallback) original[i]).getAttribute());
377                ((PagePropertiesCallback) copy[i]).setInfoText(
378                ((PagePropertiesCallback) original[i]).getInfoText());
379                if (debug.messageEnabled()) {
380                    debug.message("clone #" + i + " is PagePropertiesCallback");
381                }
382            } else if (original[i] instanceof ChoiceCallback) {
383
384                ChoiceCallback originalChoiceCallback = (ChoiceCallback) original[i];
385
386                ChoiceCallback clone = new ChoiceCallback(
387                    originalChoiceCallback.getPrompt(),
388                    originalChoiceCallback.getChoices(),
389                    originalChoiceCallback.getDefaultChoice(),
390                    originalChoiceCallback.allowMultipleSelections());
391
392                if (originalChoiceCallback.getSelectedIndexes() != null
393                        && originalChoiceCallback.getSelectedIndexes().length > 0) {
394                    if (originalChoiceCallback.allowMultipleSelections()) {
395                        clone.setSelectedIndexes(originalChoiceCallback.getSelectedIndexes());
396                    } else {
397                        clone.setSelectedIndex(originalChoiceCallback.getSelectedIndexes()[0]);
398                    }
399                }
400
401                copy[i] = clone;
402                extCallbacks.add(copy[i]);
403                if (debug.messageEnabled()) {
404                    debug.message("clone #" + i + " is ChoiceCallback");
405                }
406            } else if (original[i] instanceof ConfirmationCallback) {
407                ConfirmationCallback temp = (ConfirmationCallback) original[i];
408                String prompt = temp.getPrompt();
409                String[] options = temp.getOptions();
410                if (prompt == null) {
411                    // no prompt
412                    if (options == null) {
413                        // no options
414                        copy[i] = new ConfirmationCallback(
415                        temp.getMessageType(),
416                        temp.getOptionType(),
417                        temp.getDefaultOption());
418                    } else {
419                        copy[i] = new ConfirmationCallback(
420                        temp.getMessageType(),
421                        options,
422                        temp.getDefaultOption());
423                    }
424                } else {
425                    // has prompt
426                    if (options == null) {
427                        // no options
428                        copy[i] = new ConfirmationCallback(
429                        prompt,
430                        temp.getMessageType(),
431                        temp.getOptionType(),
432                        temp.getDefaultOption());
433                    } else {
434                        copy[i] = new ConfirmationCallback(
435                        prompt,
436                        temp.getMessageType(),
437                        options,
438                        temp.getDefaultOption());
439                    }
440                }
441                extCallbacks.add(copy[i]);
442                if (debug.messageEnabled()) {
443                    debug.message("clone #" + i + " is ConfirmationCallback");
444                }
445            } else if (original[i] instanceof TextInputCallback) {
446                copy[i] = new TextInputCallback(
447                ((TextInputCallback) original[i]).getPrompt());
448                extCallbacks.add(copy[i]);
449                if (debug.messageEnabled()) {
450                    debug.message("clone #" + i + " is TextInputCallback");
451                }
452            } else if (original[i] instanceof HttpCallback) {
453                HttpCallback hc = (HttpCallback) original[i];
454                copy[i] = new HttpCallback(hc.getAuthorizationHeader(),
455                hc.getNegotiationHeaderName(),
456                hc.getNegotiationHeaderValue(),
457                hc.getNegotiationCode());
458                extCallbacks.add(copy[i]);
459            } else if (original[i] instanceof RedirectCallback) {
460                RedirectCallback rc = (RedirectCallback) original[i];
461                copy[i] = new RedirectCallback(rc.getRedirectUrl(),
462                rc.getRedirectData(),
463                rc.getMethod(),
464                rc.getStatusParameter(),
465                rc.getRedirectBackUrlCookieName());
466                extCallbacks.add(copy[i]);
467            } else {
468                debug.error("unknown callback " + original[i]);
469            }
470            // more callbacks need to be handled here if ...
471        }
472        
473        // construct external Callback[]
474        Callback[] ext = new Callback[extCallbacks.size()];
475        if (!extCallbacks.isEmpty()) {
476            Iterator it = extCallbacks.iterator();
477            int i = 0;
478            while (it.hasNext()) {
479                ext[i++] = (Callback) it.next();
480            }
481        }
482        
483        // set external/internal callbacks
484        internal.set(index, copy);
485        external.set(index, ext);
486        
487        return ext;
488    }
489    
490    /**
491     * Returns an administration SSOToken for use the OpenAM APIs.
492     *
493     * <I>NB:</I>This is not the SSOToken that represents the user, if you wish
494     * to set/get user session properties use the <code>setUserSessionProperty</code>
495     * and <code>getUserSessionProperty</code> method respectively.
496     *
497     * @return An administrative <code>SSOToken</code>.
498     * @exception AuthLoginException if the authentication SSO session
499     *         is null.
500     * @supported.api
501     */
502    public SSOToken getSSOSession() throws AuthLoginException {
503        SSOToken sess = AuthD.getAuth().getSSOAuthSession();
504        if (sess == null) {
505            throw new AuthLoginException(bundleName, "nullSess", null);
506        }
507        return sess;
508    }
509    
510    /**
511     * Returns a Callback array for a specific state.
512     * <p>
513     * This method can be used to retrieve Callback[] for any state. All
514     * previous submitted Callback[] information are kept until the login
515     * process is completed.
516     * @param index  order of state
517     * @return Callback array for this state, return 0-length Callback array
518     *     if there is no Callback defined for this state
519     * @throws AuthLoginException if unable to read the callbacks
520     * @supported.api
521     */
522    public Callback[] getCallback(int index) throws AuthLoginException {
523        return getCallback(index, false);
524    }
525
526    /**
527     * Return a Callback array for a specific state.
528     * <p>
529     * This method can be used to retrieve Callback[] for any state. All
530     * previous submitted Callback[] information are kept until the login
531     * process is completed.
532     * @param index     order of state
533     * @param fetchOrig boolean indicating even if the callbacks for this
534     *        state have been previously retrieved, get the original callbacks
535     *        from AMModuleProperties, if set to "true".
536     * @return Callback array for this state, return 0-length Callback array
537     *     if there is no Callback defined for this state
538     * @throws AuthLoginException if unable to read the callbacks
539     * @supported.api
540     */
541    public Callback[] getCallback(int index, boolean fetchOrig) 
542        throws AuthLoginException 
543    {
544        // This method will be called by customer module, so it will
545        // return Callback[] from external callback List
546        // check if there is no callbacks defined for this module
547        if (noCallbacks || ( (isSharedState) && (!forceCallbacksRead) )) {
548            return EMPTY_CALLBACK;
549        }
550        
551        if ((internal == null) || ( fetchOrig )) {
552            forceCallbacksInit();
553            if (origList == null || origList.isEmpty()) {
554                return EMPTY_CALLBACK;
555            }
556
557            if (debug.messageEnabled()) {
558                debug.message("callback size for state " + index + "=" +
559                stateLength);
560            }
561        }
562        
563        // get Callback[] for this page
564        // use index-1 as order since page index starts with 1
565        if (index > stateLength) {
566            // invalid login state
567            debug.error("getCallback, state " + index + " > " + stateLength);
568            throw new AuthLoginException(bundleName, "invalidState",
569            new Object[]{new Integer(index)});
570        }
571        Object temp = external.get(index-1);
572        if (temp != null) {
573            return (Callback[]) temp;
574        }
575        
576        // callbacks has not been retrieved for this index yet
577        // need to get it from AMModuleProperties
578        // since the Callbacks could not be shared by different instances
579        // we need to create clone copy here
580        return cloneCallbacks(index-1, (Callback[]) origList.get(index-1));
581    }
582
583    protected void forceCallbacksInit () throws AuthLoginException {
584        if (internal == null) {
585            // get the callbacks for this class;
586            origList = AMModuleProperties.getModuleProperties(fileName);
587            if (origList == null || origList.isEmpty()) {
588                // we got file whose size is zero, this is the case for
589                // Cert/Anonymous based authentication
590                noCallbacks = true;
591                return;
592            }
593            // instantiate internal/external according to module callback size
594            stateLength = origList.size();
595            internal = new ArrayList();
596            external = new ArrayList();
597            if (debug.messageEnabled()) {
598                debug.message("callback stateLength in file = " + stateLength);
599            }
600            for (int i = 0; i < stateLength; i++) {
601                internal.add(null);
602                external.add(null);
603            }
604        }
605    }
606    
607    /**
608     * Replace Callback object for a specific state.
609     * @param state Order of login state
610     * @param index Index of Callback in the Callback array to be replaced
611     *      for the specified state. Here index starts with 0, i.e. 0 means the
612     *      first Callback in the Callback[], 1 means the second callback.
613     * @param callback Callback instance to be replaced
614     * @exception AuthLoginException if state or index is out of 
615     *         bound, or callback instance is null.
616     * @supported.api
617     */
618    public void replaceCallback(int state, int index, Callback callback)
619    throws AuthLoginException {
620        if (debug.messageEnabled()) {
621            debug.message("ReplaceCallback : state=" + state + ", index=" +
622            index + ", callback=" + callback);
623        }
624        // check state length
625        if (state > stateLength) {
626            throw new AuthLoginException(bundleName, "invalidState",
627            new Object[]{new Integer(state)});
628        }
629        // check callback length for the state
630        Callback[] ext = getCallback(state);
631        if (index < 0 || index >= ext.length) {
632            throw new AuthLoginException(bundleName, "invalidCallbackIndex",
633            new Object[]{new Integer(index)});
634        }
635        // check callback instance
636        if (callback == null) {
637            throw new AuthLoginException(bundleName, "nullCallback", null);
638        }
639        
640        // replace callback in external & internal Callback array
641        ext[index] = callback;
642        // in internal, first Callback is always PagePropertiesCallback
643        // so add one here for the index
644        ((Callback[]) internal.get(state-1))[index + 1] = callback;
645    }
646    
647    /**
648     * Replace page header for a specific state.
649     * @param state  Order of login state
650     * @param header header messages to be replaced
651     * @throws AuthLoginException if state is out of bound.
652     */
653    public void replaceHeader(int state, String header)
654    throws AuthLoginException {
655        if (debug.messageEnabled()) {
656            debug.message("ReplaceHeader : state=" + state + ", header=" +
657            header);
658        }
659
660        if (lastState != state) {
661            alreadyReplaced = false;
662        }
663        lastState = state;
664
665        // check state length
666        if (state > stateLength) {
667            throw new AuthLoginException(bundleName, "invalidState",
668            new Object[]{new Integer(state)});
669        }
670        // check callback length for the state
671        Callback[] ext = getCallback(state, true);
672        if (ext.length<=0) {
673            throw new AuthLoginException(bundleName, "invalidCallbackIndex",
674            null);
675        }
676        
677        // in internal, first Callback is always PagePropertiesCallback
678        if ((header!=null)&&(header.length() != 0)) {
679            PagePropertiesCallback pc =
680            (PagePropertiesCallback)((Callback[]) internal.get(state-1))[0];
681            // retrieve header with REPLACE tag
682            if ( !(alreadyReplaced) ) {
683                headerWithReplaceTag = pc.getHeader();
684            }
685            // replace string
686            int idx = headerWithReplaceTag.indexOf("#REPLACE#");
687            if (idx != -1) {
688                String newHeader = headerWithReplaceTag.substring(0, idx) + header;
689                pc.setHeader(newHeader);
690                alreadyReplaced = true;
691            }else{
692                String newHeader = headerWithReplaceTag.substring(0, 
693                    headerWithReplaceTag.indexOf("<BR></BR>")) + "<BR></BR>" + header;
694                pc.setHeader(newHeader);                
695            }
696        }
697    }
698    
699    /**
700     * Allows you to set the info text for a specific callback. Info Text is shown
701     * under the element in the Login page. It is used in the membership module to
702     * implement in-line feedback.
703     * 
704     * @param state state in which the Callback[] to be reset
705     * @param callback the callback to associate the info text
706     * @param infoText the infotext for the callback
707     * @throws AuthLoginException if state/callback is out of bounds
708     * @supported.api
709     */
710    public void substituteInfoText(int state, int callback, String infoText) 
711    throws AuthLoginException {
712        if (debug.messageEnabled()) {
713            debug.message("setInfoText : state=" + state + ", infoText=" + infoText);
714        }
715        
716        // check state length
717        if (state > stateLength) {
718            throw new AuthLoginException(bundleName, "invalidState",
719                new Object[]{new Integer(state)});
720        }
721        
722        // check callback length for the state
723        Callback[] ext = getCallback(state);
724        if (ext.length<=0) {
725            throw new AuthLoginException(bundleName, "invalidCallbackIndex", null);
726        }
727
728        // in internal, first Callback is always PagePropertiesCallback
729        if ((infoText != null) && (infoText.length() != 0)) {
730            PagePropertiesCallback pc =
731            (PagePropertiesCallback)((Callback[]) internal.get(state - 1))[0];
732
733            // substitute string
734            List<String> infoTexts = pc.getInfoText();
735            infoTexts.set(callback, infoText);
736            pc.setInfoText(infoTexts);
737        }     
738    }
739    
740    /**
741     * Clears the info text for a given callback state
742     * 
743     * @param state The state to clear all infotexts
744     * @throws AuthLoginException Invalid state
745     * @supported.api
746     */
747    public void clearInfoText(int state)
748    throws AuthLoginException {
749        if (debug.messageEnabled()) {
750            debug.message("clearInfoText : state=" + state);
751        }        
752        
753        // check state length
754        if (state > stateLength) {
755            throw new AuthLoginException(bundleName, "invalidState",
756                new Object[]{new Integer(state)});
757        }
758        
759        // check callback length for the state
760        Callback[] ext = getCallback(state);
761        if (ext.length<=0) {
762            throw new AuthLoginException(bundleName, "invalidCallbackIndex", null);
763        }
764        
765        // in internal, first Callback is always PagePropertiesCallback
766        PagePropertiesCallback pc =
767            (PagePropertiesCallback)((Callback[]) internal.get(state - 1))[0];
768
769        // clear info text
770        List<String> infoTexts = pc.getInfoText();
771        
772        for (int i = 0; i < infoTexts.size(); i++) {
773            infoTexts.set(i, EMPTY_STRING);
774        }
775        
776        pc.setInfoText(infoTexts);
777    }
778
779    /**
780     * Use this method to replace the header text from the XML file with new
781     * text. This method can be used multiple times on the same state replacing
782     * text with new text each time. Useful for modules that control their own
783     * error handling.
784     *
785     * @param state state state in which the Callback[] to be reset
786     * @param header The text of the header to be replaced
787     * @throws AuthLoginException if state is out of bounds
788     * @supported.api
789     */
790    public void substituteHeader(int state, String header)
791    throws AuthLoginException {
792        if (debug.messageEnabled()) {
793            debug.message("substituteHeader : state=" + state + ", header=" +
794            header);
795        }
796        // check state length
797        if (state > stateLength) {
798            throw new AuthLoginException(bundleName, "invalidState",
799            new Object[]{new Integer(state)});
800        }
801        // check callback length for the state
802        Callback[] ext = getCallback(state);
803        if (ext.length<=0) {
804            throw new AuthLoginException(bundleName, "invalidCallbackIndex",
805            null);
806        }
807
808        // in internal, first Callback is always PagePropertiesCallback
809        if ((header!=null)&&(header.length() != 0)) {
810            PagePropertiesCallback pc =
811            (PagePropertiesCallback)((Callback[]) internal.get(state-1))[0];
812
813            // substitute string
814            pc.setHeader(header);
815        }
816    }
817    
818    /**
819     * Reset a Callback instance to the original Callback for the specified
820     * state and the specified index. This will override change to the Callback
821     * instance by the <code>replaceCallback()</code> method.
822     * @param state state in which the Callback[] to be reset
823     * @param index index order of the Callback in the Callback[], index starts
824     *        with 0, i.e. 0 means first callback instance, 1 means
825     *        the second callback instance.
826     * @throws AuthLoginException if state or index is out of bound.
827     * @supported.api
828     */
829    public void resetCallback(int state, int index)
830    throws AuthLoginException {
831        if (debug.messageEnabled()) {
832            debug.message("resetCallback: state=" + state + ",index=" + index);
833        }
834        // check state length
835        if (state > stateLength) {
836            throw new AuthLoginException(bundleName, "invalidState",
837            new Object[]{new Integer(state)});
838        }
839        // check callback length for the state
840        Callback[] ext = getCallback(state);
841        if (index < 0 || index >= ext.length) {
842            throw new AuthLoginException(bundleName, "invalidCallbackIndex",
843            new Object[]{new Integer(index)});
844        }
845        
846        // get the Callback from AMModuleProperties
847        // add one to index here since first one is the PagePropertiesCallback
848        Callback callback = ((Callback[]) origList.get(state-1))[index+1];
849        Callback newCallback = null;
850        if (callback instanceof NameCallback) {
851            newCallback = new NameCallback(
852            ((NameCallback) callback).getPrompt());
853        } else if (callback instanceof PasswordCallback) {
854            newCallback = new PasswordCallback(
855            ((PasswordCallback) callback).getPrompt(),
856            ((PasswordCallback) callback).isEchoOn());
857        } else if (callback instanceof ChoiceCallback) {
858            int selection = ((ChoiceCallback) callback).getDefaultChoice();
859            newCallback = new ChoiceCallback(
860            ((ChoiceCallback) callback).getPrompt(),
861            ((ChoiceCallback) callback).getChoices(),
862            selection,
863            ((ChoiceCallback) callback).allowMultipleSelections());
864        } else {
865            // should never come here since only above three will be supported
866            debug.error("Unsupported call back instance " + callback);
867            throw new AuthLoginException(bundleName, "unknownCallback", null);
868        }
869        
870        if (debug.messageEnabled()) {
871            debug.message("original=" + callback + ",new=" + newCallback);
872        }
873        
874        // set external & internal callback instance
875        ((Callback[]) internal.get(state-1))[index+1] = newCallback;
876        ((Callback[]) external.get(state-1))[index] = newCallback;
877    }
878    
879    /**
880     * Implements initialize() method in JAAS LoginModule class.
881     * <p>
882     * The purpose of this method is to initialize Login Module,
883     * it will call the init() method implemented by user's Login
884     * Module to do initialization.
885     * <p>
886     * This is a final method.
887     * @param subject - the Subject to be authenticated.
888     * @param callbackHandler - a CallbackHandler for communicating with the
889     *     end user (prompting for usernames and passwords, for example).
890     * @param sharedState - state shared with other configured LoginModules.
891     * @param options - options specified in the login Configuration for this
892     *     particular LoginModule.
893     */
894    public final void initialize(Subject subject,
895    CallbackHandler callbackHandler,
896    java.util.Map sharedState,
897    java.util.Map options) {
898        this.subject = subject;
899        this.handler = callbackHandler;
900        this.sharedState = sharedState;
901        this.options = options;
902        // get class name
903        String className = this.getClass().getName();
904        int index = className.lastIndexOf(".");
905        moduleClass = className.substring(index + 1);
906        moduleName = (String) options.get(ISAuthConstants.
907        MODULE_INSTANCE_NAME);
908        
909        // get module properties file path
910        
911        loginState = getLoginState();
912        
913        fileName = loginState.getFileName(moduleClass+ ".xml");
914        loginState.setSharedState(sharedState);
915        
916        // get resource bundle
917        
918        bundle = amCache.getResBundle(bundleName, getLoginLocale());
919        
920        if (debug.messageEnabled()) {
921            debug.message("AMLoginModule resbundle locale="+getLoginLocale());
922            debug.message("Login, class = " + className +
923            ", module=" + moduleName + ", file=" + fileName);
924        }
925        isSharedState = Boolean.valueOf(CollectionHelper.getMapAttr(
926            options, ISAuthConstants.SHARED_STATE_ENABLED, "false")
927            ).booleanValue();
928        
929        isStore = Boolean.valueOf(CollectionHelper.getMapAttr(
930            options, ISAuthConstants.STORE_SHARED_STATE_ENABLED, "true")
931            ).booleanValue();
932
933        sharedStateBehaviorPattern = Misc.getMapAttr(options,
934            ISAuthConstants.SHARED_STATE_BEHAVIOR_PATTERN,
935            "tryFirstPass");
936        
937        if (debug.messageEnabled()) {
938            debug.message("AMLoginModule" +
939            ISAuthConstants.SHARED_STATE_BEHAVIOR_PATTERN +
940            " is set to " + sharedStateBehaviorPattern);
941        }        
942       
943        // Check for composite Advice
944        String compositeAdvice = loginState.getCompositeAdvice();
945        if (compositeAdvice != null) {
946            if (debug.messageEnabled()) {
947                debug.message("AMLoginModule.initialize: "
948                    + "Adding Composite Advice " + compositeAdvice);
949            }
950            sharedState.put(ISAuthConstants.COMPOSITE_ADVICE_XML,
951                compositeAdvice);
952        } 
953        // call customer init method
954        init(subject, sharedState, options);
955    }
956    
957    /**
958     * Initialize this LoginModule.
959     * <p>
960     * This is an abstract method, must be implemented by user's Login Module
961     * to initialize this LoginModule with the relevant information. If this
962     * LoginModule does not understand any of the data stored in sharedState
963     * or options parameters, they can be ignored.
964     * @param subject - the Subject to be authenticated.
965     * @param sharedState - state shared with other configured LoginModules.
966     * @param options - options specified in the login Configuration for this
967     *     particular LoginModule. It contains all the global and organization
968     *     attribute configuration for this module. The key of the map is the
969     *     attribute name (e.g. <code>iplanet-am-auth-ldap-server</code>) as
970     *     String, the value is the value of the corresponding attribute as Set.
971     * @supported.api
972     */
973    abstract public void init(Subject subject,
974    java.util.Map sharedState,
975    java.util.Map options);
976    
977    /**
978     * Abstract method must be implemented by each login module to
979     * control the flow of the login process.
980     * <p>
981     * This method takes an array of sbumitted
982     * Callback, process them and decide the order of next state to go.
983     * Return -1 if the login is successful, return 0 if the
984     * LoginModule should be ignored.
985     * @param callbacks Callback[] for this Login state
986     * @param state  Order of state. State order starts with 1.
987     * @return order of next state.  return -1 if authentication
988     *     is successful, return 0 if the LoginModule should be ignored.
989     * @exception LoginException if login fails.
990     * @supported.api
991     */
992    abstract public int process(Callback[] callbacks, int state)
993    throws LoginException;
994    
995    /**
996     * Abstract method must be implemeted by each login module to
997     * get the user Principal
998     * @return Principal
999     * @supported.api
1000     */
1001    abstract public java.security.Principal getPrincipal();
1002    
1003    /**
1004     * This method should be overridden by each login module
1005     * to destroy dispensable state fields.
1006     *
1007     * @supported.api
1008     */
1009    public void  destroyModuleState(){};
1010    
1011    /**
1012     * This method should be overridden by each login module
1013     * to do some garbage collection work after the module
1014     * process is done. Typically those class wide global variables
1015     * that will not be used again until a logout call should be nullified.
1016     */
1017    public void  nullifyUsedVars() {};
1018    
1019    /**
1020     * Wrapper for process() to utilize AuthLoginException.
1021     * @param callbacks associated with authentication
1022     * @param state of callbacks
1023     * @return state of auth login
1024     * @exception AuthLoginException if login fails.
1025     */
1026    private int wrapProcess(Callback[] callbacks, int state)
1027    throws AuthLoginException {
1028        try {
1029            if (callbacks != null) {
1030                for (int i = 0; i < callbacks.length; i++) {
1031                    if (callbacks[i] instanceof NameCallback) {
1032                        String newUser = null;
1033                        try {
1034                            newUser = IdUtils.getIdentityName(
1035                                ((NameCallback) callbacks[i]).getName(), 
1036                                getRequestOrg());
1037                        } catch (IdRepoException idRepoExp) {
1038                            //Print message and let Auth proceed.
1039                            debug.message(
1040                                "AMLoginModule.wrapProcess: Cannot get "+
1041                                "username from idrepo. ", idRepoExp);
1042                        } 
1043                        if (newUser != null) {
1044                            ((NameCallback) callbacks[i]).setName(newUser);
1045                        }
1046                    }
1047                }
1048            }
1049            return process(callbacks, state);
1050        } catch (InvalidPasswordException e) {
1051            setFailureState();
1052            setFailureID(e.getTokenId());
1053            throw e;
1054        } catch (AuthLoginException e) {
1055            setFailureState();
1056            throw e;
1057        } catch (LoginException e) {
1058            setFailureState();
1059            throw new AuthLoginException(e);
1060        } catch (RuntimeException re) {
1061            setFailureState();
1062            throw re;
1063        }
1064    }
1065
1066    private void setFailureState() {
1067        currentState = ISAuthConstants.LOGIN_IGNORE;
1068        setFailureModuleName(moduleName);
1069    }
1070    
1071    /**
1072     * Returns true if a module in authentication chain has already done, either
1073     * succeeded or failed.
1074     *
1075     * @return true if a module in authentication chain has already done, either
1076     * succeeded or failed.
1077     */
1078    private boolean moduleHasDone() {
1079        return (currentState == ISAuthConstants.LOGIN_SUCCEED) ||
1080        (currentState == ISAuthConstants.LOGIN_IGNORE);
1081    }
1082    
1083    /**
1084     * Implements login() method in JAAS LoginModule class.
1085     * <p>
1086     * This method is responsible for retrieving corresponding Callback[] for
1087     * current state, send as requirement to user, get the submitted Callback[],
1088     * call the process() method. The process() method will decide the next
1089     * action based on those submitted Callback[].
1090     * <p>
1091     * This method is final.
1092     * @return <code>true</code> if the authentication succeeded, or 
1093         *         <code>false</code> if this LoginModule should be ignored.
1094     * @throws AuthLoginException - if the authentication fails
1095     */
1096    public final boolean login() throws AuthLoginException {
1097        if (moduleHasDone()) {
1098            debug.message("This module has already done.");
1099            if (currentState == ISAuthConstants.LOGIN_SUCCEED) {
1100                return true;
1101            } else {
1102                return false;
1103            }
1104        } else {
1105            if (debug.messageEnabled()) {
1106                debug.message("This module is not done yet. CurrentState: "
1107                + currentState);
1108            }
1109        }
1110        
1111        // make one getCallback call to populate first state
1112        // this will set the noCallbacks variable
1113        if (internal == null) {
1114            getCallback(1);
1115        }
1116        // if this module does not define any Callbacks (such as Cert),
1117        // pass control right to module, then check return code from module
1118        if (noCallbacks) {
1119            currentState =  wrapProcess(EMPTY_CALLBACK, 1);
1120            // check login status
1121            if (currentState == ISAuthConstants.LOGIN_SUCCEED) {
1122                setSuccessModuleName(moduleName);
1123                succeeded = true;
1124                nullifyUsedVars();
1125                return true;
1126            } else if (currentState == ISAuthConstants.LOGIN_IGNORE) {
1127                // index = 0;
1128                setFailureModuleName(moduleName);
1129                succeeded = false;
1130                destroyModuleState();
1131                principal = null;
1132                return false;
1133            } else {
1134                setFailureModuleName(moduleName);
1135                succeeded = false;
1136                cleanup();
1137                throw new AuthLoginException(bundleName, "invalidCode",
1138                new Object[]{new Integer(currentState)});
1139            }
1140        }
1141        
1142        if (handler == null) {
1143            debug.error("Handler is null");
1144            throw new AuthLoginException(bundleName, "nullHandler", null);
1145        }
1146        try {
1147            Callback[] lastCallbacks = null;
1148            boolean needToExit = false;
1149            
1150            // starting from first page
1151            //currentState = 1;
1152            while (currentState != ISAuthConstants.LOGIN_SUCCEED &&
1153            currentState != ISAuthConstants.LOGIN_IGNORE) {
1154                if (debug.messageEnabled()) {
1155                    debug.message("Login, state = " + currentState);
1156                }
1157                if (isSharedState) {
1158                    currentState =  wrapProcess(EMPTY_CALLBACK, 1);
1159                    isSharedState = false;
1160                    continue;
1161                }
1162                // get current set of callbacks
1163                getCallback(currentState);
1164                // check if this is an error state, if so, throw exception
1165                // to terminate login process
1166                Callback[] cbks = ((Callback[]) internal.get(currentState-1));
1167                PagePropertiesCallback callback =
1168                (PagePropertiesCallback) cbks[0];
1169                
1170                if (callback.getErrorState()) {
1171                    // this is an error state
1172                    setFailureModuleName(moduleName);
1173                    String template = callback.getTemplateName();
1174                    String errorMessage = callback.getHeader();
1175                    if (template == null || template.length() == 0) {
1176                        // this is the case which no error template is
1177                        // defined, only exception message in header
1178                        throw new MessageLoginException(errorMessage);
1179                    } else {
1180                        // send error template
1181                        //setLoginFailureURL(template);
1182                        setModuleErrorTemplate(template);
1183                        throw new AuthLoginException(errorMessage);
1184                    }
1185                }
1186                // call handler to handle the internal callbacks
1187                handler.handle(cbks);
1188                
1189                // Get the page state from the PagePropertiesCallback
1190                Callback[] cbksPrev= ((Callback[])internal.get(currentState-1));
1191                
1192                PagePropertiesCallback callbackPrev =
1193                (PagePropertiesCallback) cbksPrev[0];
1194                String pageState = callbackPrev.getPageState();
1195                if ((pageState != null) &&
1196                (pageState.length() != 0) &&
1197                (!pageState.equals(Integer.toString(currentState)))) {
1198                    int loginPage = Integer.parseInt(pageState);
1199                    
1200                    //Set the current page state in PagePropertiesCallback
1201                    callbackPrev.setPageState(Integer.toString(currentState));
1202                    
1203                    currentState = loginPage;
1204                    if (debug.messageEnabled()) {
1205                        debug.message("currentState from UI " + currentState);
1206                    }
1207                }
1208                
1209                // Get the last submitted callbacks to auth module and submit
1210                // those callbacks to do DataStore authentication if the incoming
1211                // user is special / internal user and auth module is other than
1212                // "DataStore" and "Application" auth modules.
1213                lastCallbacks = (Callback[])external.get(currentState-1);
1214                if ((!moduleName.equalsIgnoreCase("DataStore")) && 
1215                        (!moduleName.equalsIgnoreCase("Application"))) {
1216                    if (!authenticateToDatastore(lastCallbacks)) {
1217                        needToExit = true;
1218                        break;
1219                    }
1220                }
1221                
1222                // send external callback and send to module for processing
1223                currentState = wrapProcess((Callback[])
1224                external.get(currentState-1), currentState);
1225                
1226                if (debug.messageEnabled()) {
1227                    debug.message("Login NEXT State : " + currentState);
1228                }
1229            }  
1230                        
1231            if (needToExit) {
1232                nullifyUsedVars();
1233                throw new AuthLoginException(AMAuthErrorCode.AUTH_MODULE_DENIED);
1234            }
1235            
1236            // check login status
1237            if (currentState == ISAuthConstants.LOGIN_SUCCEED) {       
1238                setSuccessModuleName(moduleName);
1239                succeeded = true;
1240                nullifyUsedVars();
1241                return true;
1242            } else {
1243                // currentState = 0;
1244                setFailureModuleName(moduleName);
1245                succeeded = false;
1246                destroyModuleState();
1247                principal = null;
1248                return false;
1249            }
1250        } catch (IOException e) {
1251            setFailureModuleName(moduleName);
1252            if (e.getMessage().equals(AMAuthErrorCode.AUTH_TIMEOUT)) {
1253                debug.message("login timed out ", e);
1254            } else {
1255                debug.message("login ", e);
1256            } throw new AuthLoginException(e);
1257        } catch (UnsupportedCallbackException e) {
1258            setFailureModuleName(moduleName);
1259            debug.message("Login", e);
1260            throw new AuthLoginException(e);
1261        }
1262    }
1263    
1264    /**
1265     * Returns authentication level that has been set for the module
1266     *
1267     * @return authentication level of this authentication session
1268     * @supported.api
1269     */
1270    public int getAuthLevel() {
1271        // get login state for this authentication session
1272        if (loginState == null) {
1273            loginState = getLoginState();
1274            if (loginState == null) {
1275                return 0;
1276            }
1277        }
1278        return loginState.getAuthLevel();
1279    }
1280    
1281    /**
1282     * Sets the <code>AuthLevel</code> for this session.
1283     * The authentication level being set cannot be downgraded
1284     * below that set by the module configuration.
1285     *
1286     * @param auth_level authentication level string to be set
1287     * @return <code>true</code> if setting is successful,<code>false</code> 
1288     *         otherwise
1289     * @supported.api
1290     */
1291    public boolean setAuthLevel(int auth_level) {
1292        // get login state for this authentication session
1293        if (loginState == null) {
1294            loginState = getLoginState();
1295            if (loginState == null) {
1296                // may be should throw AuthLoginException here
1297                debug.error("Unable to set auth level : " + auth_level);
1298                return false;
1299            }
1300        }
1301        loginState.setModuleAuthLevel(auth_level);
1302        return true;
1303    }
1304    
1305    /**
1306     * Returns the current state in the authentication process.
1307     *
1308     * @return the current state in the authentication process.
1309     * @supported.api
1310     */
1311    public int getCurrentState() {
1312        return currentState;
1313    }
1314    
1315    /**
1316     * Returns the <code>HttpServletRequest</code> object that
1317     * initiated the call to this module.
1318     *
1319     * @return <code>HttpServletRequest</code> for this request, returns null
1320     *         if the <code>HttpServletRequest</code> object could not be
1321     *         obtained.
1322     * @supported.api
1323     */
1324    public HttpServletRequest getHttpServletRequest() {
1325        // get login state for this authentication session
1326        if (loginState == null) {
1327            loginState = getLoginState();
1328            if (loginState == null) {
1329                return null;
1330            }
1331        }
1332        return loginState.getHttpServletRequest();
1333    }
1334    
1335    /**
1336     * Returns the authentication <code>LoginState</code>
1337     * @param methodName Name of the required methd in 
1338     *        <code>LoginState</code> object
1339     * @return <code>com.sun.identity.authentication.service.LoginState</code>
1340     *         for this authentication method.
1341     * @throws AuthLoginException if fails to get the Login state
1342     */
1343    protected com.sun.identity.authentication.service.LoginState getLoginState(
1344    String methodName) throws AuthLoginException {
1345        if (loginState == null) {
1346            loginState = getLoginState();
1347            if (loginState == null) {
1348                throw new AuthLoginException(bundleName, "wrongCall",
1349                new Object[]{methodName});
1350            }
1351        }
1352        return loginState;
1353    }
1354    
1355    /**
1356     * Returns the Login <code>Locale</code> for this session
1357     * @return <code>Locale</code> used for localizing text
1358     */
1359    protected java.util.Locale getLoginLocale()  {
1360        try {
1361            String loc = getLocale();
1362            return com.sun.identity.shared.locale.Locale.getLocale(loc);
1363        } catch (AuthLoginException ex) {
1364            debug.message("unable to determine loginlocale ", ex);
1365            return java.util.Locale.ENGLISH;
1366        }
1367    }
1368    
1369    /*
1370     * Returns the Login State object
1371     * @return com.sun.identity.authentication.service.LoginState
1372     */
1373    private com.sun.identity.authentication.service.LoginState getLoginState() {
1374        Callback[] callbacks = new Callback[1];
1375        try {
1376            callbacks[0] = new LoginStateCallback();
1377            if (handler == null) {
1378                return null;
1379            }
1380            handler.handle(callbacks);
1381            return ((LoginStateCallback) callbacks[0]).getLoginState();
1382        } catch (Exception e) {
1383            debug.message("Error.." ,e );
1384            return null;
1385        }
1386    }
1387    
1388    /**
1389     * Returns the <code>HttpServletResponse</code> object for the servlet
1390     * request that initiated the call to this module. The servlet response
1391     * object will be the response to the <code>HttpServletRequest</code>
1392     * received by the authentication module.
1393     *
1394     * @return <code>HttpServletResponse</code> for this request, returns null
1395     * if the <code>HttpServletResponse</code> object could not be obtained.
1396     * @supported.api
1397     */
1398    public HttpServletResponse getHttpServletResponse() {
1399        // get login state for this authentication session
1400        if (loginState == null) {
1401            loginState = getLoginState();
1402            if (loginState == null) {
1403                return null;
1404            }
1405        }
1406        return loginState.getHttpServletResponse();
1407    }
1408    
1409    /**
1410     * Returns the CallbackHandler object for the module. This method
1411     * will be used internally.
1412     *
1413     * @return CallbackHandler for this request, returns null if the
1414     *         CallbackHandler object could not be obtained.
1415     */
1416    public CallbackHandler getCallbackHandler() {
1417        return handler;
1418    }
1419    
1420    /**
1421     * Returns the locale for this authentication session.
1422     *
1423     * @return <code>java.util.Locale</code> locale for this authentication
1424     *         session.
1425     * @throws AuthLoginException if problem in accessing the 
1426               locale.
1427     * @supported.api
1428     */
1429    public String getLocale() throws AuthLoginException {
1430        // get login state for this authentication session
1431        return  getLoginState("getLocale()").getLocale();
1432    }
1433    
1434    /**
1435     * Returns the number of authentication states for this
1436     * login module.
1437     *
1438     * @return the number of authentication states for this login module.
1439     * @supported.api
1440     */
1441    public int getNumberOfStates() {
1442        return stateLength;
1443    }
1444    
1445    /**
1446     * Returns the organization DN for this authentication session.
1447     *
1448     * @return organization DN.
1449     * @supported.api
1450     */
1451    public String getRequestOrg() {
1452        // get login state for this authentication session
1453        if (loginState == null) {
1454            loginState = getLoginState();
1455            if (loginState == null) {
1456                return null;
1457            }
1458        }
1459        return loginState.getOrgDN();
1460    }
1461    
1462    /**
1463     * Returns a unique key for this authentication session.
1464     * This key will be unique throughout an entire Web browser session.
1465     *
1466     * @return null is unable to get the key,
1467     * @supported.api
1468     */
1469    public String getSessionId() {
1470        // get login state for this authentication session
1471        if (loginState == null) {
1472            loginState = getLoginState();
1473            if (loginState == null) {
1474                return null;
1475            }
1476        }
1477       return loginState.getSid().toString();
1478/*
1479        InternalSession sess = loginState.getSession();
1480        if (sess != null) {
1481            return sess.getID().toString();
1482        } else {
1483            return null;
1484        }
1485*/
1486    }
1487    
1488    /**
1489     * Returns the organization attributes for specified organization.
1490     *
1491     * @param orgDN Requested organization DN.
1492     * @return Map that contains all attribute key/value pairs defined
1493     *         in the organization.
1494     * @throws AuthLoginException if cannot get organization profile.
1495     * @supported.api
1496     */
1497    public Map getOrgProfile(String orgDN) throws AuthLoginException {
1498        Map orgMap = null;
1499        if (orgDN == null || orgDN.length() == 0) {
1500            // get login state for this authentication session
1501            orgDN = getLoginState("getOrgProfile(String)").getOrgDN();
1502        }
1503        
1504        try {
1505           OrganizationConfigManager orgConfigMgr =
1506                AuthD.getAuth().getOrgConfigManager(orgDN);
1507            orgMap = orgConfigMgr.getAttributes(
1508                ISAuthConstants.IDREPO_SVC_NAME);
1509           if (debug.messageEnabled()) {
1510              debug.message("orgMap is : " + orgMap);
1511           }
1512        } catch (Exception ex) {
1513            debug.message("getOrgProfile", ex);
1514            throw new AuthLoginException(ex);
1515        }
1516        return orgMap;
1517    }
1518    
1519    /**
1520     * Returns service template attributes defined for the specified
1521     * organization.
1522     *
1523     * @param orgDN Organization DN.
1524     * @param serviceName Requested service name.
1525     * @return Map that contains all attribute key/value pairs defined in the
1526     *         organization service template.
1527     * @throws AuthLoginException if cannot get organization service
1528     *         template.
1529     * @supported.api
1530     */
1531    public Map getOrgServiceTemplate(String orgDN, String serviceName)
1532            throws AuthLoginException {
1533        Map orgMap = null;
1534        if (orgDN == null || orgDN.length() == 0) {
1535            // get login state for this authentication session
1536            orgDN = getLoginState(
1537            "getOrgServiceTemplate(String, String)").getOrgDN();
1538        }
1539        try {
1540           OrganizationConfigManager orgConfigMgr = 
1541                            AuthD.getAuth().getOrgConfigManager(orgDN);
1542           orgMap = orgConfigMgr.getServiceConfig(serviceName).getAttributes();
1543        }
1544        catch (Exception ex) {
1545            debug.message("getOrgServiceTemplate", ex);
1546            throw new AuthLoginException(ex);
1547        }
1548        return orgMap;
1549    }
1550
1551    /**
1552     * Checks if dynamic profile creation is enabled.
1553     *
1554     * @return <code>true</code> if dynamic profile creation is enabled.
1555     */
1556    public boolean isDynamicProfileCreationEnabled() {
1557        // get login state for this authentication session
1558        if (loginState == null) {
1559            loginState = getLoginState();
1560            if (loginState == null) {
1561                return false;
1562            }
1563        }
1564        return loginState.isDynamicProfileCreationEnabled();
1565    }
1566    
1567    /**
1568     * Returns service configuration attributes.
1569     * @param name Requested service name.
1570     * @return Map that contains all attribute key/value pairs defined in
1571     *         the service configuration.
1572     * @throws AuthLoginException if error in accessing the service schema.
1573     *
1574     * @supported.api
1575     */
1576    public Map getServiceConfig(String name) throws AuthLoginException {
1577        try {
1578            ServiceSchemaManager scm = new ServiceSchemaManager(name,
1579            AuthD.getAuth().getSSOAuthSession());
1580            ServiceSchema sc = scm.getGlobalSchema();
1581            HashMap retMap = new HashMap();
1582            if (sc != null) {
1583                retMap.putAll(sc.getAttributeDefaults());
1584            }
1585            
1586            sc = scm.getOrganizationSchema();
1587            if (sc != null) {
1588                retMap.putAll(sc.getAttributeDefaults());
1589            }
1590            
1591            sc = scm.getUserSchema();
1592            if (sc != null) {
1593                retMap.putAll(sc.getAttributeDefaults());
1594            }
1595            
1596            sc = scm.getPolicySchema();
1597            if (sc != null) {
1598                retMap.putAll(sc.getAttributeDefaults());
1599            }
1600            
1601            return retMap;
1602        } catch (Exception ex) {
1603            debug.message("getServiceConfig", ex);
1604            throw new AuthLoginException(ex);
1605        }
1606    }
1607    
1608    /**
1609     * Returns the user profile for the user specified. This
1610     * method may only be called in the validate() method.
1611     *
1612     * @param userDN distinguished name os user.
1613     * @return <code>AMUser</code> object for the user's distinguished name.
1614     * @throws AuthLoginException if it fails to get the user profile for
1615     *         <code>userDN</code>.
1616     * @deprecated This method has been deprecated. Please use the
1617     *             IdRepo API's to get the AMIdentity object for the user. More
1618     *             information on how to use the Identity Repository APIs is
1619     *             available in the "Customizing Identity Data Storage" chapter
1620     *             of the OpenAM Developer's Guide.
1621     *
1622     * @supported.api
1623     */
1624     public AMUser getUserProfile(String userDN) throws AuthLoginException{
1625        AMUser user = null;
1626        try {
1627            user = AuthD.getAuth().getSDK().getUser(userDN);
1628        } catch (Exception ex) {
1629            debug.message("getUserProfile", ex);
1630            throw new AuthLoginException(ex);
1631        }
1632        return user;
1633    }
1634    
1635    /**
1636     * Returns the property from the user session. If the session is being force
1637     * upgraded then set on the old session otherwise set on the current session.
1638     *
1639     * @param name The property name.
1640     * @return The property value.
1641     * @throws AuthLoginException if the user session is invalid.
1642     *
1643     * @supported.api
1644     */
1645    public String getUserSessionProperty(String name)
1646            throws AuthLoginException {
1647        InternalSession sess = null;
1648
1649        if (getLoginState(null).isSessionUpgrade() &&
1650                getLoginState(null).getForceFlag()) {
1651            sess = getLoginState(null).getOldSession();
1652        } else {
1653            sess = getLoginState("getUserSessionProperty()").getSession();
1654        }
1655
1656        if (sess != null) {
1657            return sess.getProperty(name);
1658        } else {
1659            return null;
1660        }
1661    }
1662    
1663    /**
1664     * Sets a property in the user session. If the session is being force
1665     * upgraded then set on the old session otherwise set on the current session.
1666     *
1667     * @param name The property name.
1668     * @param value The property value.
1669     * @throws AuthLoginException if the user session is invalid.
1670     *
1671     * @supported.api
1672     */
1673    public void setUserSessionProperty(String name, String value)
1674            throws AuthLoginException {
1675        InternalSession sess = null;
1676
1677        if (getLoginState(null).isSessionUpgrade() &&
1678                getLoginState(null).getForceFlag()) {
1679            sess = getLoginState(null).getOldSession();
1680        } else {
1681            sess = getLoginState("setUserSessionProperty()").getSession();
1682        }
1683
1684        if (sess != null) {
1685            sess.putProperty(name, value);
1686        } else {
1687            throw new AuthLoginException(bundleName, "wrongCall",
1688            new Object[]{" setUserSessionProperty()"});
1689        }
1690    }
1691    
1692    /**
1693     * Returns a set of user IDs generated from the class defined
1694     * in the Core Authentication Service. Returns null if the
1695     * attribute <code>iplanet-am-auth-username-generator-enabled</code> is
1696     * set to false.
1697     *
1698     * @param attributes the keys in the <code>Map</code> contains the
1699     *        attribute names and their corresponding values in
1700     *        the <code>Map</code> is a <code>Set</code> that
1701     *        contains the values for the attribute
1702     * @param num the maximum number of returned user IDs; 0 means there
1703     *        is no limit
1704     * @return a set of auto-generated user IDs
1705     * @throws AuthLoginException if the class instantiation failed
1706     *
1707     * @supported.api
1708     */
1709    public Set getNewUserIDs(Map attributes, int num)
1710            throws AuthLoginException {
1711        boolean enabled = getLoginState(
1712                "getNewUserIDs(Map, int)").isUserIDGeneratorEnabled();
1713        
1714        if (!enabled) {
1715            return null;
1716        }
1717        
1718        String className = getLoginState(
1719                "getNewUserIDs(Map, int)").getUserIDGeneratorClassName();
1720        String orgDN = getLoginState("getNewUserIDs(Map, int)").getOrgDN();
1721        
1722        // if className is null or empty, use the default user ID
1723        // generator class name
1724        if (className == null || className.length() == 0) {
1725            className = ISAuthConstants.DEFAULT_USERID_GENERATOR_CLASS;
1726        }
1727        
1728        UserIDGenerator idGenerator = null;
1729        try {
1730            // instantiate the Java class
1731            Class theClass = Class.forName(className);
1732            idGenerator = (UserIDGenerator)theClass.newInstance();
1733            
1734        } catch (Exception e) {
1735            debug.message("getNewUserIDs(): unable to instantiate " +
1736            className, e);
1737            return null;
1738        }
1739        
1740        return (idGenerator.generateUserIDs(orgDN, attributes, num));
1741    }
1742    
1743    /**
1744     * Sets the the login failure URL for the user. This method does not
1745     * change the URL in the user's profile. When the user authenticates
1746     * failed, this URL will be used by the authentication for the
1747     * redirect.
1748     *
1749     * @param url URL to go when authentication failed.
1750     * @throws AuthLoginException if unable to set the URL.
1751     *
1752     * @supported.api
1753     */
1754    public void setLoginFailureURL(String url) throws AuthLoginException {
1755        getLoginState("setLoginFailureURL()").setFailureLoginURL(url);
1756    }
1757    
1758    /**
1759     * Sets the error template for the module
1760     * @param templateName the error template for the module
1761     * @throws AuthLoginException when unable to set the template 
1762     */
1763    public void setModuleErrorTemplate(String templateName)
1764        throws AuthLoginException {
1765        getLoginState(
1766            "setModuleTemplate()").setModuleErrorTemplate(templateName);
1767    }
1768    
1769    /**
1770     * Sets the the login successful URL for the user. This method does not
1771     * change the URL in the user's profile. When the user authenticates
1772     * successfully, this URL will be used by the authentication for the
1773     * redirect.
1774     *
1775     * @param url <code>URL</code> to go when authentication is successful.
1776     * @throws AuthLoginException if unable to set the URL.
1777     * @supported.api
1778     */
1779    public void setLoginSuccessURL(String url) throws AuthLoginException {
1780        getLoginState("setLoginSuccessURL()").setSuccessLoginURL(url);
1781    }
1782    
1783    /**
1784     * Sets the user organization. This method should only be called when the
1785     * user authenticates successfully. It allows the user authentication
1786     * module to decide in which domain the user profile should be created.
1787     *
1788     * @param orgDN The organization DN.
1789     * @throws AuthLoginException
1790     */
1791    public void setOrg(String orgDN) throws AuthLoginException {
1792/* TODO
1793        if (orgDN.indexOf("=") == -1) {
1794            throw new AuthLoginException(bundleName, "invalidDN",
1795                new Object[]{orgDN});
1796        }
1797        getLoginState("setOrg()").setOrg(orgDN);
1798 */
1799        return;
1800    }
1801    
1802    /**
1803     * Checks if a Callback is required to have input.
1804     * @param state Order of state.
1805     * @param index Order of the Callback in the Callback[], the index.
1806     *        starts with 0.
1807     * @return <code>true</code> if the callback corresponding to the number
1808     *         in the specified state is required to have value,
1809     *         <code>false</code> otherwise
1810     * @supported.api
1811     */
1812    public boolean isRequired(int state, int index) {
1813        // check state
1814        if (state > stateLength) {
1815            // invalid state, return false now
1816            return false;
1817        }
1818        // get internal callbacks for the state
1819        Callback[] callbacks = (Callback[]) internal.get(state - 1);
1820        if (callbacks == null || callbacks.length == 0) {
1821            // no callbacks defined for this state, return false
1822            return false;
1823        }
1824        // check first Callback
1825        Callback callback = callbacks[0];
1826        if (callback instanceof PagePropertiesCallback) {
1827            List req = ((PagePropertiesCallback) callback).getRequire();
1828            if (req == null || req.isEmpty() || index >= req.size()) {
1829                return false;
1830            } else {
1831                String tmp = (String) req.get(index);
1832                if (tmp.equalsIgnoreCase("true")) {
1833                    return true;
1834                } else {
1835                    return false;
1836                }
1837            }
1838        } else {
1839            return false;
1840        }
1841    }
1842    
1843    /**
1844     * Returns the info text associated with a specific callback
1845     * 
1846     * @param state The state to fetch the info text
1847     * @param index The callback to fetch the info text
1848     * @return The info text
1849     * @supported.api
1850     */
1851    public String getInfoText(int state, int index) {
1852        // check state
1853        if (state > stateLength) {
1854            // invalid state, return empty string now
1855            return EMPTY_STRING;
1856        }
1857        // get internal callbacks for the state
1858        Callback[] callbacks = (Callback[]) internal.get(state - 1);
1859        if (callbacks == null || callbacks.length == 0) {
1860            // no callbacks defined for this state, return empty string
1861            return EMPTY_STRING;
1862        }
1863        // check first Callback
1864        Callback callback = callbacks[0];
1865        if (callback instanceof PagePropertiesCallback) {
1866            List<String> infoText = ((PagePropertiesCallback) callback).getAttribute();
1867            if (infoText == null || infoText.isEmpty() || index >= infoText.size()) {
1868                return EMPTY_STRING;
1869            } else {
1870                return infoText.get(index);
1871            }
1872        } else {
1873            return EMPTY_STRING;
1874        }
1875    }
1876    
1877    /**
1878     * Returns the attribute name for the specified callback in the
1879     * specified login state.
1880     *
1881     * @param state Order of state
1882     * @param index Order of the Callback in the Callback[], the index
1883     *        starts with 0.
1884     * @return Name of the attribute, empty string will be returned
1885     *         if the attribute is not defined.
1886     * @supported.api
1887     */
1888    public String getAttribute(int state, int index) {
1889        // check state
1890        if (state > stateLength) {
1891            // invalid state, return empty string now
1892            return EMPTY_STRING;
1893        }
1894        // get internal callbacks for the state
1895        Callback[] callbacks = (Callback[]) internal.get(state - 1);
1896        if (callbacks == null || callbacks.length == 0) {
1897            // no callbacks defined for this state, return empty string
1898            return EMPTY_STRING;
1899        }
1900        // check first Callback
1901        Callback callback = callbacks[0];
1902        if (callback instanceof PagePropertiesCallback) {
1903            List req = ((PagePropertiesCallback) callback).getAttribute();
1904            if (req == null || req.isEmpty() || index >= req.size()) {
1905                return EMPTY_STRING;
1906            } else {
1907                return (String) req.get(index);
1908            }
1909        } else {
1910            return EMPTY_STRING;
1911        }
1912    }
1913    
1914    /**
1915     * Aborts the authentication process.
1916     * <p>
1917     * This JAAS LoginModule method must be implemented by user's module.
1918     * <p>
1919     * This method is called if the overall authentication
1920     * failed. (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
1921     * LoginModules did not succeed).
1922     * If this LoginModule's own authentication attempt succeeded (checked by
1923     * retrieving the private state saved by the login method), then this
1924     * method cleans up any state that was originally saved.
1925     *
1926     * @return <code>true</code> if this method succeeded,<code>false</code>
1927     *         if this LoginModule should be ignored.
1928     * @throws AuthLoginException if the abort fails
1929     * @see javax.security.auth.spi.LoginModule#abort
1930     */
1931    public final boolean abort() throws AuthLoginException {
1932        debug.message("ABORT return.... false");
1933        if (succeeded == false) {
1934            return false;
1935        } else {
1936            logout();
1937        }
1938        return true;
1939    }
1940    
1941    /**
1942     * Commit the authentication process (phase 2).
1943     * <p>
1944     * This JAAS LoginModule method must be implemented by user's module.
1945     * <p>
1946     * This method is called if the overall authentication
1947     * succeeded (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
1948     * LoginModules succeeded).
1949     * <p>
1950     * If this LoginModule's own authentication attempt succeeded (checked by
1951     * retrieving the private state saved by the login method), then this
1952     * method associates relevant Principals and Credentials with the Subject
1953     * located in the LoginModule. If this LoginModule's own authentication
1954     * attempted failed, then this method removes/destroys any state that was
1955     * originally saved.
1956     *
1957     * @return <code>true</code> if this method succeeded, or <code>false</code>
1958     *         if this <code>LoginModule</code> should be ignored.
1959     * @throws AuthLoginException if the commit fails
1960     * @see javax.security.auth.spi.LoginModule#commit
1961     */
1962    public final boolean commit() throws AuthLoginException {
1963        principal = getPrincipal();
1964        if (debug.messageEnabled()) {
1965            debug.message(
1966                "AMLoginModule.commit():Succeed,principal=" + principal);
1967        }
1968        if (succeeded == false || principal == null) {
1969            return false;
1970        } else if (!subject.getPrincipals().contains(principal)) {
1971            subject.getPrincipals().add(principal);
1972            debug.message("Done added user to principal");
1973        }
1974        cleanup();
1975        return true;
1976    }
1977    
1978    /**
1979     * Logs out a Subject.
1980     * <p>
1981     * This JAAS LoginModule method must be implemented by user's module.
1982     * <p>
1983     * An implementation of this method might remove/destroy a Subject's
1984     * Principals and Credentials.
1985     *
1986     * @return <code>true</code> if this method succeeded, or <code>false</code>
1987     *         if this LoginModule should be ignored.
1988     * @throws AuthLoginException if the logout fails
1989     * @see javax.security.auth.spi.LoginModule#logout
1990     */
1991    public final boolean logout() throws AuthLoginException {
1992        // logging out
1993        if (subject.getPrincipals().contains(principal)) {
1994            subject.getPrincipals().remove(principal);
1995        }
1996        succeeded = false;
1997        cleanup();
1998        return true;
1999    }
2000    
2001    /**
2002     * Sets the <code>userID</code> of user who failed authentication.
2003     * This <code>userID</code> will be used to log failed authentication in
2004     * the OpenSSO error logs.
2005     *
2006     * @param userID user name of user who failed authentication.
2007     * @supported.api
2008     */
2009    public void setFailureID(String userID) {
2010        // get login state for this authentication session
2011        if (userID == null) {
2012            return;
2013        }
2014        debug.message("setFailureID : " + userID);
2015        if (loginState == null) {
2016            loginState = getLoginState();
2017            if (loginState == null) {
2018                // may be should throw AuthLoginException here
2019                debug.error("Unable to set set userId : " + userID);
2020                return;
2021            }
2022        }
2023        loginState.setFailedUserId(userID);
2024        return ;
2025    }
2026    
2027    /**
2028     * Sets a Map of attribute value pairs to be used when the authentication
2029     * service is configured to dynamically create a user.
2030     *
2031     * @param attributeValuePairs A map containing the attributes
2032     * and its values. The key is the attribute name and the value
2033     * is a Set of values.
2034     *
2035     * @supported.api
2036     */
2037    public void setUserAttributes(Map attributeValuePairs) {
2038        // get login state for this authentication session
2039        if (loginState == null) {
2040            loginState = getLoginState();
2041            if (loginState == null) {
2042                debug.error("Unable to set user attributes");
2043                return;
2044            }
2045        }
2046        loginState.setUserCreationAttributes(attributeValuePairs);
2047        return;
2048    }
2049    
2050    /**
2051     * Validates the given user name by using validation plugin if exists
2052     * else it checks invalid characters in the source string.
2053     *
2054     * @param userName source string which should be validated.
2055     * @param regEx the pattern for which to search.
2056     * @throws UserNamePasswordValidationException if user name is invalid.
2057     * @supported.api
2058     */
2059    public void validateUserName(String userName, String regEx)
2060            throws UserNamePasswordValidationException {
2061        try {
2062            AMUserPasswordValidation plugin = getUPValidationInstance();
2063            if (plugin != null) {
2064                debug.message("Validating username...");
2065                Map envMap = new HashMap(2);
2066                envMap.put(
2067                 com.sun.identity.shared.Constants.ORGANIZATION_NAME,
2068                 getRequestOrg());
2069                plugin.validateUserID(userName, envMap);
2070            } else if (regEx != null && (regEx.length() != 0)) {
2071                if (! (ISValidation.validate(userName, regEx, debug))) {
2072                    throw new UserNamePasswordValidationException(bundleName,
2073                    "invalidChars", null);
2074                }
2075            }
2076        } catch (AMException ame) {
2077            if (debug.messageEnabled()) {
2078                debug.message("User Name validation Failed" + ame.getMessage());
2079            }
2080            throw new UserNamePasswordValidationException(ame);
2081        } catch (Exception ex) {
2082            debug.message(
2083            "unKnown Exception occured during username validation");
2084            throw new UserNamePasswordValidationException(ex);
2085        }
2086    }
2087    
2088    
2089    /**
2090     * Sets the moduleName of successful LoginModule.
2091     * This moduleName will be populated in the session
2092     * property "AuthType"
2093     * @param moduleName name of module
2094     */
2095    private void setSuccessModuleName(String moduleName) {
2096        // get login state for this authentication session
2097        if (loginState == null) {
2098            loginState = getLoginState();
2099            if (loginState == null) {
2100                debug.error("Unable to set moduleName : " + moduleName);
2101                return;
2102            }
2103        }
2104        if (debug.messageEnabled()) {
2105            debug.message("SETTING Module name.... :" + moduleName);
2106        }
2107        loginState.setSuccessModuleName(moduleName);
2108        loginState.saveSharedStateAttributes();
2109        if (getPrincipal() != null && getPrincipal().getName() != null) {
2110            loginState.saveAuthenticatedPrincipal(getPrincipal().getName());
2111        }
2112
2113        auditor.auditModuleSuccess(loginState, getPrincipal(), getAuditEntryDetail());
2114    }
2115    
2116    /**
2117     * Checks if valid user exists.
2118     *
2119     * @param userDN the distinguished name of the user.
2120     * @return <code>true</code> if user exists,<code>false</code>otherwise
2121     */
2122    public boolean isValidUserEntry(String userDN)  {
2123        // TODO - IdRepo does not have an equivalent of this
2124        // this method is mainly called to validate DSAME Users
2125        // which are going to be processed differently.
2126
2127        boolean isValidUser = false;
2128        try {
2129            isValidUser = 
2130                (AuthD.getAuth().getIdentity(IdType.USER, userDN, "/") != null);
2131        } catch (AuthException e) {
2132            debug.message("User Valid :" + isValidUser);
2133        }
2134        return isValidUser;
2135    }
2136    
2137    /**
2138     * Checks if distinguished user name is a super admin.
2139     *
2140     * @param userDN the distinguished name of the user.
2141     * @return <code>true</code> if distinguished user name is a super admin.
2142     */
2143    public boolean isSuperAdmin(String userDN)  {
2144        boolean isSuperAdmin = AuthD.getAuth().isSuperAdmin(userDN);
2145        if (debug.messageEnabled()) {
2146            debug.message("is SuperAdmin : " + isSuperAdmin);
2147        }
2148        return isSuperAdmin;
2149    }
2150    
2151    /**
2152     * Validate password for the distinguished user, this will use validation 
2153     * plugin if exists to validate password
2154     *
2155     * @param userPassword source string which should be validated.
2156     * @throws UserNamePasswordValidationException if user password is invalid.
2157     *
2158     * @supported.api
2159     */
2160    public void validatePassword(String userPassword)
2161    throws UserNamePasswordValidationException {
2162        AMUserPasswordValidation plugin = getUPValidationInstance();
2163        try {
2164            if (plugin != null) {
2165                if (debug.messageEnabled()) {
2166                    debug.message("Validating password...");
2167                }
2168                
2169                plugin.validatePassword(userPassword);
2170            } else {
2171                if (debug.messageEnabled()) {
2172                    debug.message("No plugin found");
2173                }
2174            }
2175        } catch (AMException ame) {
2176            if (debug.messageEnabled()) {
2177                debug.message("Password validation Failed " + ame.getMessage());
2178            }
2179            
2180            throw new UserNamePasswordValidationException(ame);
2181        } catch (Exception ex) {
2182            if (debug.messageEnabled()) {
2183                debug.message("Unknown Exception occured during password validation");
2184            }
2185            
2186            throw new UserNamePasswordValidationException(ex);
2187        }
2188    }
2189    
2190    /*
2191     * this method instantiates and returns plugin object
2192     */
2193    private AMUserPasswordValidation getUPValidationInstance() {
2194        
2195        try {
2196            String className ;
2197            String orgDN = getRequestOrg();
2198            if (orgDN != null){
2199                className = getOrgPluginClassName(orgDN);
2200            }
2201            else {
2202                className = getPluginClassName();
2203            }
2204
2205            if (debug.messageEnabled()) {
2206                debug.message("UserPasswordValidation Class Name is : " +
2207                className);
2208            }
2209            if ( (className == null) || (className.length() == 0)) {
2210                return null;
2211            }
2212            
2213            AMUserPasswordValidation userPasswordInstance =
2214            (AMUserPasswordValidation)
2215            (Class.forName(className).newInstance());
2216            return userPasswordInstance;
2217        } catch (ClassNotFoundException ce) {
2218            if (debug.messageEnabled()) {
2219                debug.message("Class not Found :", ce);
2220            }
2221            return null;
2222        } catch (Exception e) {
2223            if (debug.messageEnabled()) {
2224                debug.message("Error: ", e);
2225            }
2226            return null;
2227        }
2228    }
2229
2230    /*
2231     * this method gets plugin classname from adminstration service for the org
2232     */
2233    private String getOrgPluginClassName(String orgDN) {
2234        try {
2235            String cachedValue = AdministrationServiceListener.
2236                getOrgPluginNameFromCache(orgDN);
2237            if (cachedValue != null) {
2238                return cachedValue;
2239            }
2240            Map config =
2241            getOrgServiceTemplate(orgDN,ISAuthConstants.ADMINISTRATION_SERVICE);
2242            String className =
2243            Misc.getServerMapAttr(config,
2244            ISAuthConstants.USERID_PASSWORD_VALIDATION_CLASS);
2245            if (debug.messageEnabled()) {
2246                debug.message("Org Plugin Class:  " + className);
2247            }
2248            AdministrationServiceListener.setOrgPluginNameInCache(
2249                orgDN, className);
2250            return className;
2251        } catch (Exception ee) {
2252            debug.message("Error while getting UserPasswordValidationClass " ,ee );
2253            return null;
2254        }
2255    }
2256    
2257    /*
2258     * this method gets plugin classname from adminstration service
2259     */
2260    private String getPluginClassName() throws AuthLoginException {
2261        String cachedValue = AdministrationServiceListener.
2262           getGlobalPluginNameFromCache();
2263        if (cachedValue != null) {
2264               return cachedValue;
2265        }
2266        Map config = getServiceConfig(ISAuthConstants.ADMINISTRATION_SERVICE);
2267        String className =
2268        CollectionHelper.getServerMapAttr(
2269            config, ISAuthConstants.USERID_PASSWORD_VALIDATION_CLASS);
2270        if (debug.messageEnabled()) {
2271            debug.message("Plugin Class:  " + className);
2272        }
2273        AdministrationServiceListener.setGlobalPluginNameInCache(
2274            className);
2275        return className;
2276    }
2277    
2278    /**
2279     * Sets the moduleName of failed login module
2280     * @param moduleName - module name of the failed module
2281     */
2282    
2283    private void setFailureModuleName(String moduleName) {
2284        // get login state for this authentication session
2285        if (loginState == null) {
2286            loginState = getLoginState();
2287            if (loginState == null) {
2288                debug.error("Unable to set moduleName : " + moduleName);
2289                return;
2290            }
2291        }
2292        if (debug.messageEnabled()) {
2293            debug.message("SETTING Failure Module name.... :" + moduleName);
2294        }
2295        loginState.setFailureModuleName(moduleName);
2296        loginState.saveSharedStateAttributes();
2297
2298        auditor.auditModuleFailure(loginState, getPrincipal(), getAuditEntryDetail());
2299    }
2300    
2301    /**
2302     * Returns JAAS shared state user key.
2303     *
2304     * @return user key.
2305     */
2306    public String getUserKey() {
2307        return ISAuthConstants.SHARED_STATE_USERNAME;
2308    }
2309    
2310    /**
2311     * Returns JAAS shared state password key.
2312     *
2313     * @return password key
2314     */
2315    public String getPwdKey() {
2316        return ISAuthConstants.SHARED_STATE_PASSWORD;
2317    }
2318    
2319    // cleanup method for Auth constants
2320    private void cleanup() {
2321        principal = null;
2322        if (sharedState !=null) {
2323            sharedState.remove(ISAuthConstants.SHARED_STATE_USERNAME);
2324            sharedState.remove(ISAuthConstants.SHARED_STATE_PASSWORD);
2325        }
2326        sharedState = null;
2327        destroyModuleState();
2328    }
2329    
2330    /**
2331     * Stores user name password into shared state map
2332     * this method should be called after successfull
2333     * authentication by each individual modules.
2334     *
2335     * @param user user name
2336     * @param passwd user password
2337     */
2338    public void storeUsernamePasswd(String user, String passwd) {
2339        // store only if store shared state is enabled
2340        if (isStore && sharedState !=null) {
2341            sharedState.put(ISAuthConstants.SHARED_STATE_USERNAME, user);
2342            sharedState.put(ISAuthConstants.SHARED_STATE_PASSWORD, passwd);
2343        }
2344    }
2345    
2346    /**
2347     * Checks if shared state enabled for the module.
2348     *
2349     * @return <code>true</code> if shared state enabled for the module.
2350     */
2351    public boolean isSharedStateEnabled() {
2352        return isSharedState;
2353    }
2354
2355    /**
2356     * Sets flag to force read call backs in auth chain process.
2357     * @param val - value to force reading call backs
2358     */
2359    public void setForceCallbacksRead(boolean val) {
2360        forceCallbacksRead = val;
2361    }
2362
2363    /**
2364     * This method returns use first pass enabled or not
2365     * @return return true if use first pass is enabled for the module
2366     */
2367    public boolean isUseFirstPassEnabled() {
2368        return (sharedStateBehaviorPattern != null) && 
2369                sharedStateBehaviorPattern.equals("useFirstPass");
2370    }
2371    
2372    /**
2373     * Returns <code>AMIdentityRepostiory</code> handle for an organization.
2374     *
2375     * @param orgDN the organization name.
2376     * @return <code>AMIdentityRepostiory</code> object
2377     */
2378    public AMIdentityRepository getAMIdentityRepository(String orgDN) {
2379       return AuthD.getAuth().getAMIdentityRepository(orgDN);
2380    }
2381
2382    /**
2383     * Creates <code>AMIdentity</code> in the repository.
2384     *
2385     * @param userName name of user to be created.
2386     * @param userAttributes Map of default attributes.
2387     * @param userRoles Set of default roles.
2388     * @throws IdRepoException
2389     * @throws SSOException
2390     */
2391    public void createIdentity(
2392        String userName,
2393        Map userAttributes,
2394        Set userRoles
2395    ) throws IdRepoException, SSOException {
2396       if (loginState == null) {
2397           loginState = getLoginState();
2398            if (loginState == null) {
2399                debug.error("Unable to create Identity: " + userName);
2400               return ;
2401            }
2402        }
2403       loginState.createUserIdentity(userName,userAttributes,userRoles);
2404       return;
2405    }
2406   
2407    /**
2408     * Get the number of failed login attempts for a user when account locking
2409     * is enabled.
2410     * @return number of failed attempts, -1 id account locking is not enabled. 
2411     * @throws AuthenticationException if the user name passed in is not valid 
2412     * or  null, or for any other error condition.
2413     * @supported.api
2414     */
2415     public int getFailCount(AMIdentity amIdUser) throws AuthenticationException {
2416         AccountLockoutInfo acInfo = null;
2417         if (loginState == null) {
2418            loginState = getLoginState();
2419            if (loginState == null) {
2420                throw new AuthenticationException(bundleName, "nullLoginState", 
2421                    null);
2422            }
2423         }
2424         ISAccountLockout isAccountLockout = new ISAccountLockout(
2425            loginState.getLoginFailureLockoutMode(),
2426            loginState.getLoginFailureLockoutTime(),
2427            loginState.getLoginFailureLockoutCount(),
2428            loginState.getLoginLockoutNotification(),
2429            loginState.getLoginLockoutUserWarning(),
2430            loginState.getLoginLockoutAttrName(),
2431            loginState.getLoginLockoutAttrValue(),
2432            loginState.getLoginFailureLockoutDuration(),
2433            loginState.getLoginFailureLockoutMultiplier(),
2434            loginState.getInvalidAttemptsDataAttrName(),
2435            bundleName);
2436         isAccountLockout.setStoreInvalidAttemptsInDS(
2437         loginState.getLoginFailureLockoutStoreInDS());
2438
2439         try {
2440             if (!isAccountLockout.isLockoutEnabled()) {
2441                  debug.message("Failure lockout mode disabled");
2442                    return -1;
2443             } else {
2444                 if (debug.messageEnabled()) {
2445                     debug.message("AMLogiModule.getFailCount()::"
2446                         +"lockout is enabled");
2447                 }
2448
2449                 String userDN = null;
2450                 userDN = normalizeDN(IdUtils.getDN(amIdUser));
2451
2452                 if (acInfo == null) {
2453                     acInfo = isAccountLockout.getAcInfo(userDN,amIdUser);
2454                 }
2455                 int failCount = acInfo.getFailCount();
2456                 if (debug.messageEnabled()) {
2457                     debug.message("AMLoginModule.getFailCount:failCount "
2458                          +"returned:" +failCount);
2459                 }
2460                 return failCount;
2461             }
2462         } catch (Exception ex) {
2463             debug.error("AMLoginModule.getFailCount:Error", ex);
2464             throw new AuthenticationException(ex.getMessage());
2465         }
2466    }
2467
2468    /**
2469     * Get the maximum number failed login attempts permitted for a user
2470     * before when their account is locked out.
2471     *
2472     * @return the maximum number of failed attempts
2473     * @supported.api
2474     */
2475    public int getMaximumFailCount()
2476    throws AuthenticationException {
2477        if (loginState == null) {
2478            loginState = getLoginState();
2479
2480            if (loginState == null) {
2481                throw new AuthenticationException(bundleName, "nullLoginState",
2482                    null);
2483            }
2484        }
2485
2486        return loginState.getLoginFailureLockoutCount();
2487    }
2488
2489    /**
2490     * Increments the fail count for the given user.
2491     *
2492     * @throws AuthenticationException if the user name passed in is not valid
2493     * or null, or for any other error condition.
2494     * @supported.api
2495     */
2496    public void incrementFailCount(String userName)
2497    throws AuthenticationException {
2498        if (loginState == null) {
2499            loginState = getLoginState();
2500
2501            if (loginState == null) {
2502                throw new AuthenticationException(bundleName, "nullLoginState",
2503                    null);
2504            }
2505        }
2506
2507        loginState.incrementFailCount(userName);
2508    }
2509
2510    /**
2511     * Returns true if the named account is locked out, false otherwise.
2512     *
2513     * @throws AuthenticationException if the user name passed in is not valid
2514     * or null, or for any other error condition.
2515     * @supported.api
2516     */
2517    public boolean isAccountLocked(String userName)
2518    throws AuthenticationException {
2519        if (loginState == null) {
2520            loginState = getLoginState();
2521
2522            if (loginState == null) {
2523                throw new AuthenticationException(bundleName, "nullLoginState",
2524                    null);
2525            }
2526        }
2527
2528        boolean accountLocked = loginState.isAccountLocked(userName);
2529
2530        if (ad.debug.messageEnabled()) {
2531            ad.debug.message("isAccountLocked for user=" + userName + " :" + accountLocked);
2532        }
2533
2534        return accountLocked;
2535    }
2536
2537    /* returns the normalized DN  */
2538    private String normalizeDN(String userDN) {
2539        String normalizedDN = userDN;
2540        if ((userDN != null) && LDAPUtils.isDN(userDN)) {
2541            normalizedDN = DNUtils.normalizeDN(userDN);
2542        }
2543        if (ad.debug.messageEnabled()) {
2544            ad.debug.message("Original DN is:" + userDN);
2545            ad.debug.message("Normalized DN is:" + normalizedDN);
2546        }
2547        return normalizedDN;
2548    }    
2549    
2550    /**
2551     * Authenticates to the datastore using idRepo API
2552     *
2553     * @param callbacks Array of last submitted callbacks to the 
2554     * authentication module
2555     * @return <code>true</code> if success. <code>false</code> if failure
2556     * @throws <code> AuthLoginException </code> 
2557     */
2558    private boolean authenticateToDatastore(Callback[] callbacks) 
2559            throws AuthLoginException {
2560        boolean retval = false;
2561        boolean needToCheck = false;
2562        Callback[] idrepoCallbacks = new Callback[2];
2563        String userName = null;
2564        char[] password = null;
2565        
2566        for (int i = 0; i < callbacks.length; i++) {
2567                if (callbacks[i] instanceof NameCallback) {
2568                    NameCallback nc = (NameCallback) callbacks[i];
2569                    userName = nc.getName();
2570                    if (debug.messageEnabled()){
2571                        debug.message("AMLoginModule.authenticateToDatastore:: "
2572                        + " user is : " + userName);
2573                        debug.message("AMLoginModule.authenticateToDatastore:: "
2574                        + " Internal users : " + LoginState.INTERNAL_USERS);
2575                    }
2576                    
2577                    if (LoginState.INTERNAL_USERS.contains(
2578                            userName.toLowerCase())) {
2579                        needToCheck = true;
2580                    } else {
2581                        break;
2582                    }
2583                    
2584                } else if (callbacks[i] instanceof PasswordCallback) {
2585                    PasswordCallback pc = (PasswordCallback) callbacks[i];
2586                    password = pc.getPassword();
2587                }
2588        }
2589        if (needToCheck == false) {
2590            return true;
2591        }
2592        
2593        if (debug.messageEnabled()){
2594            debug.message("AMLoginModule.authenticateToDatastore:: "
2595                + "Authenticating Internal user to configuration store");
2596        }
2597        NameCallback nameCallback = new NameCallback("NamePrompt");
2598        nameCallback.setName(userName);
2599        idrepoCallbacks[0] = nameCallback;
2600        PasswordCallback passwordCallback = new PasswordCallback(
2601            "PasswordPrompt",false);
2602        passwordCallback.setPassword(password);
2603        idrepoCallbacks[1] = passwordCallback;
2604        try {
2605            AMIdentityRepository idrepo = getAMIdentityRepository(
2606                getRequestOrg());
2607            retval = idrepo.authenticate(idrepoCallbacks);
2608            if (debug.messageEnabled()){
2609                debug.message("AMLoginModule.authenticateToDatastore:: " + 
2610                    " IDRepo authentication successful");
2611            }
2612        } catch (IdRepoException idrepoExp) {
2613            if (debug.messageEnabled()){
2614                debug.message("AMLoginModule.authenticateToDatastore::  "
2615                    + "IdRepo Exception : ", idrepoExp);
2616            }
2617        } catch (InvalidPasswordException ipe) {
2618            throw new AuthLoginException(AMAuthErrorCode.AUTH_MODULE_DENIED);
2619        }
2620        return retval;
2621
2622    }
2623
2624    /**
2625     * Returns true if the user identified by the supplied username has reached
2626     * their session quota.<br>
2627     * <i>NB</i>The existing session count is exclusive of any session created
2628     * as part of the running authentication process
2629     *
2630     * @param userName the username of the user who's session quota will be checked
2631     * @return true if the user session quota is reached, false otherwise
2632     * @supported.api
2633     */
2634    public boolean isSessionQuotaReached(String userName) {
2635        int sessionCount = -1;
2636        int sessionQuota = -1;
2637
2638        if (userName == null || userName.equals(Constants.EMPTY)) {
2639            debug.error("AMLoginModule.isSessionQuotaReached :: called with null username");
2640            return false;
2641        }
2642
2643        try {
2644            // Get the universal ID
2645            AMIdentity amIdUser = ad.getIdentity(IdType.USER, userName,
2646                    loginState.getOrgDN());
2647
2648            String univId = IdUtils.getUniversalId(amIdUser);
2649
2650            if (univId != null) {
2651                sessionQuota = getSessionQuota(amIdUser);
2652                sessionCount = SessionCount.getAllSessionsByUUID(univId).size();
2653
2654                if (debug.messageEnabled()) {
2655                    debug.message("AMLoginModule.isSessionQuotaReached :: univId= "
2656                            + univId + " - Session Quota Reached =  " + (sessionCount >= sessionQuota));
2657                }
2658            } else {
2659                debug.error("AMLoginModule.isSessionQuotaReached :: "
2660                        + "univId is null , amIdUser is " + amIdUser);
2661                return false;
2662            }
2663        } catch (Exception ex) {
2664            debug.error("AMLoginModule.getSessionQuotaLevel::  "
2665                    + "Exception : ", ex);
2666        }
2667
2668        return (sessionCount >= sessionQuota);
2669    }
2670
2671    private int getSessionQuota(AMIdentity iden) {
2672        int quota = SessionConstraint.getDefaultSessionQuota();
2673        
2674        if (iden == null) {
2675            debug.error("AMLoginModule.getSessionQuota :: AMIdentity is null, returning default quota");
2676            return quota;
2677        }
2678
2679        try {
2680             Map serviceAttrs =
2681                iden.getServiceAttributesAscending("iPlanetAMSessionService");
2682
2683             Set s = (Set)serviceAttrs.get("iplanet-am-session-quota-limit");
2684             Iterator attrs = s.iterator();
2685             if (attrs.hasNext()) {
2686                String attr = (String) attrs.next();
2687                quota = (Integer.valueOf(attr)).intValue();
2688             }
2689        } catch (Exception ex) {
2690            debug.error("Failed to get the session quota via the "+
2691                        "IDRepo interfaces, => Use the default " +
2692                        "value from the dynamic schema instead.", ex);
2693        }
2694
2695        return quota;
2696   }
2697
2698
2699    /**
2700     * Returns the set of SSOTokens for a specified user
2701     *
2702     * @param userName The username to be used to query the sessions
2703     * @return The set of SSOTokens for the user's current sessions, returns null on error
2704     * @supported.api
2705     */
2706    public Set<SSOToken> getUserSessions(String userName) {
2707        Set<SSOToken> sessions = new HashSet<SSOToken>();
2708
2709        if (userName == null || userName.equals(Constants.EMPTY)) {
2710            debug.error("AMLoginModule.getUserSessions :: called with null username");
2711            return null;
2712        }
2713
2714        try {
2715            // Get the universal ID
2716            AMIdentity amIdUser = ad.getIdentity(IdType.USER, userName, loginState.getOrgDN());
2717
2718            String univId = IdUtils.getUniversalId(amIdUser);
2719
2720            if (univId != null) {
2721                Map<String, String> currentSessions = SessionCount.getAllSessionsByUUID(univId);
2722                SSOTokenManager manager = SSOTokenManager.getInstance();
2723
2724                for (String tokenID : currentSessions.keySet()) {
2725                    sessions.add(manager.createSSOToken(tokenID));
2726                }
2727
2728                if (debug.messageEnabled()) {
2729                    debug.message("AMLoginModule.getUserSessions :: univId= "
2730                            + univId + " - found sessions =  " + sessions);
2731                }
2732            } else {
2733                debug.error("AMLoginModule.getUserSessions :: "
2734                        + "univId is null , amIdUser is " + amIdUser);
2735                return null;
2736            }
2737        } catch (Exception ex) {
2738            debug.error("AMLoginModule.getUserSessions::  "
2739                    + "Exception : ", ex);
2740        }
2741
2742        return sessions;
2743    }
2744
2745    /**
2746     * Provides the "Alias Search Attribute Name" list from the Authentication
2747     * Service for the realm. If these attributes are not configured it falls
2748     * back to the User Naming Attribute for the realm
2749     * @return a set containing the attribute names configured
2750     */
2751    protected Set<String> getUserAliasList() throws AuthLoginException {
2752        final Map<String, Set<String>> orgSvc = getOrgServiceTemplate(getRequestOrg(), ISAuthConstants.AUTH_SERVICE_NAME);
2753        Set<String> aliasAttrNames = orgSvc.get(ISAuthConstants.AUTH_ALIAS_ATTR);
2754        if (debug.messageEnabled()) {
2755            debug.message("AMLoginModule.getUserAliasList: from " + ISAuthConstants.AUTH_ALIAS_ATTR + ": "+ aliasAttrNames);
2756        }
2757        if (aliasAttrNames.isEmpty()) {
2758            aliasAttrNames = orgSvc.get(ISAuthConstants.AUTH_NAMING_ATTR);
2759            if (debug.messageEnabled()) {
2760                debug.message("AMLoginModule.getUserAliasList: from " + ISAuthConstants.AUTH_NAMING_ATTR +": "
2761                        + aliasAttrNames);
2762            }
2763        }
2764        return aliasAttrNames;
2765    }
2766
2767    /**
2768     * Returns the principals authenticated in the current authentication process or an empty set if login state is
2769     * unavailable or no authenticated principals are present.
2770     * 
2771     * @return a set of authenticated principals.
2772     */
2773    protected Set<String> getAuthenticatedPrincipals() {
2774        if (loginState == null) {
2775            loginState = getLoginState();
2776        }
2777        if (loginState == null) {
2778            if (debug.messageEnabled()) {
2779                debug.message("AMLoginModule.getAuthenticatedPrincipals: ubable to get loginState");
2780            }
2781            return Collections.emptySet();
2782        }
2783        return loginState.getAuthenticatedPrincipals();
2784    }
2785
2786    /**
2787     * Supply the additional detail to be logged with this module's completion event. Subclasses can override this
2788     * method to add more specific detail.
2789     *
2790     * @return The audit entry detail.
2791     */
2792    protected AuthenticationAuditEntry getAuditEntryDetail() {
2793        AuthenticationAuditEntry entryDetail = new AuthenticationAuditEntry();
2794        entryDetail.setModuleId(moduleName);
2795
2796        String ip = loginState.getClient();
2797        if (isNotEmpty(ip)) {
2798            entryDetail.addInfo(IP_ADDRESS, ip);
2799        }
2800        AuthContext.IndexType indexType = loginState.getIndexType();
2801        if (indexType != null) {
2802            entryDetail.addInfo(AUTH_INDEX, indexType.toString());
2803        }
2804        entryDetail.addInfo(AUTH_LEVEL, String.valueOf(getAuthLevel()));
2805        entryDetail.addInfo(MODULE_CLASS, moduleClass);
2806
2807        return entryDetail;
2808    }
2809}