001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: AuthContext.java,v 1.10 2009/01/28 05:34:52 ww203982 Exp $
026 *
027 */
028
029/**
030 * Portions Copyrighted [2011] [ForgeRock AS]
031 */
032package com.sun.identity.authentication.internal;
033
034import java.io.ByteArrayInputStream;
035import java.io.ByteArrayOutputStream;
036import java.io.IOException;
037import java.io.ObjectInputStream;
038import java.io.ObjectOutputStream;
039import java.net.InetAddress;
040import java.security.Principal;
041import java.security.SecureRandom;
042import java.util.Iterator;
043import java.util.Set;
044
045import javax.security.auth.callback.Callback;
046import javax.security.auth.callback.ChoiceCallback;
047import javax.security.auth.callback.NameCallback;
048import javax.security.auth.callback.PasswordCallback;
049import javax.security.auth.callback.TextInputCallback;
050import javax.security.auth.callback.TextOutputCallback;
051import javax.security.auth.login.LoginException;
052
053import com.sun.identity.shared.ldap.util.DN;
054import com.sun.identity.shared.ldap.LDAPDN;
055
056import com.sun.identity.shared.debug.Debug;
057import com.iplanet.am.util.SecureRandomManager;
058import com.iplanet.am.util.SystemProperties;
059import com.iplanet.services.util.I18n;
060import com.iplanet.sso.SSOToken;
061import com.sun.identity.authentication.internal.server.AuthSPrincipal;
062import com.sun.identity.authentication.internal.util.AuthI18n;
063import com.sun.identity.shared.Constants;
064import com.sun.identity.sm.ServiceManager;
065
066
067/**
068 * The AuthContext provides the implementation for authenticating users using
069 * the JAAS technology. It complements <code>LoginContext
070 * </code> provided by
071 * JAAS by supporting organization environments that cannot handle sessions, for
072 * example, HTTP/HTML.
073 * <p>
074 * A typical caller instantiates this class and starts the login process. The
075 * caller then obtains an array of <code>Callback</code> objects, which
076 * contains the information required by the authentication plug-in module. The
077 * caller requests information from the user. On receiving the information from
078 * the user, the caller submits the same to this class. If more information is
079 * required, the above process continues until all the information required by
080 * the plug-ins has been supplied. The caller then checks if the user has
081 * successfully been authenticated. If successfully authenticated, the caller
082 * can then get the <code>
083 * Subject</code> for the user; if not successfully
084 * authenticated, the caller obtains the LoginException.
085 *
086 * @supported.api
087 */
088public final class AuthContext extends Object {
089
090    /**
091     * This login status indicates that the login process
092     * has not started yet. Basically, it means that the method
093     * <code>startLogin</code> has not been called.
094     *
095     * @supported.api
096     */
097    public static final int AUTH_NOT_STARTED = 1;
098
099    /**
100     * This login status indicates that the login process
101     * is in progress. Basically, it means that the <code>startLogin</code>
102     * method has been called and that this object is waiting for the user to
103     * send authentication information.
104     *
105     * @supported.api
106     */
107    public static final int AUTH_IN_PROGRESS = 2;
108
109    /**
110     * This login status indicates that the login process
111     * has succeeded.
112     *
113     * @supported.api
114     */
115    public static final int AUTH_SUCCESS = 3;
116
117    /**
118     * This login status indicates that the login process
119     * has failed.
120     *
121     * @supported.api
122     */
123    public static final int AUTH_FAILED = 4;
124
125    /**
126     * This login status indicates that the user has been
127     * successfully logged out.
128     *
129     * @supported.api
130     */
131    public static final int AUTH_COMPLETED = 5;
132
133    /*
134     * Protected variables used locally
135     */
136    protected final String authComponentName = "Authentication";
137
138    protected final static String authKeyName = "authContext";
139
140    // Debug class
141    protected final static String authDebugName = "amAuthInternal";
142
143    protected static Debug authDebug = Debug.getInstance(authDebugName);
144
145    protected String organizationName = null;
146
147    protected String applicationName = null;
148
149    protected int loginStatus;
150
151    protected LoginException loginException;
152
153    protected Callback[] informationRequired;
154
155    protected Callback[] submittedInformation;
156
157    protected AuthLoginThread loginThread;
158
159    protected LoginContext loginContext;
160
161    protected SSOToken token;
162
163    protected static I18n myAuthI18n = AuthI18n.authI18n;
164
165    private static boolean isEnableHostLookUp = Boolean.valueOf(
166            SystemProperties.get(Constants.ENABLE_HOST_LOOKUP)).booleanValue();
167
168    //
169    // overall, AuthContext is a "conduit" between the application and the
170    // login module. the Principal implementation must be agreed upon at
171    // those two endpoints; AuthContext just passes the Subject that contains
172    // the Principal(s).
173    //
174
175    /**
176     * Constructor to get an instance of
177     * <code>AuthContext</code>. Caller would then use
178     * <code>getRequirements()</code> and <code>submitRequirements()</code>
179     * to pass the credentials needed for authentication by the plugin modules.
180     * 
181     * @throws LoginException
182     *
183     * @supported.api
184     */
185    public AuthContext() throws LoginException {
186        // initialize
187        this("");
188    }
189
190    /**
191     * Constructor to get an authenticated instance
192     * of this class given the <code>java.security.Principal</code> the user
193     * would like to be authenticated as, and the <code>password</code> for
194     * the user.
195     * 
196     * @param principal
197     *            name of the user to be authenticated
198     * @param password
199     *            password for the user
200     * @throws LoginException
201     *
202     * @supported.api
203     */
204    public AuthContext(Principal principal, char[] password)
205            throws LoginException {
206        this(null, principal, password);
207    }
208
209    /*
210     * Constructor for DPro to provide hostname and port for LDAP
211     * authentication.
212     */
213    public AuthContext(Principal principal, char[] password, String hostname,
214            int port) throws LoginException {
215        this(LoginContext.LDAP_AUTH_URL + hostname + ":" + port, principal,
216                password);
217    }
218
219    /**
220     * Constructor to get an instance of this class
221     * given the organization name <code>orgName</code> the user would like to
222     * access, the <code>java.security.Principal
223     * </code>the user would like to
224     * be authenticated as, and the <code>password</code> for the user.
225     * 
226     * @param orgName
227     *            name of the user's organization
228     * @param principal
229     *            name of the user to be authenticated
230     * @param password
231     *            password for the user
232     * @throws LoginException
233     *
234     * @supported.api
235     */
236    public AuthContext(String orgName, Principal principal, char[] password)
237            throws LoginException {
238        // Make sure principal and password are not null
239        if (principal == null)
240            throw (new LoginException(myAuthI18n
241                    .getString("com.iplanet.auth.invalid-username")));
242        if (password == null)
243            throw (new LoginException(myAuthI18n
244                    .getString("com.iplanet.auth.invalid-password")));
245
246        AuthSubject subject = new AuthSubject();
247        
248        if (orgName != null)
249            organizationName = orgName;
250        reset(subject);
251
252        // Set the username and password in LoginContext's sharedState
253        loginContext.updateSharedState(principal.getName(), password);
254
255        boolean gotName = false;
256        boolean gotPassword = false;
257        Callback[] callbacks;
258
259        if (authDebug.messageEnabled()) {
260            authDebug.message("Instantiated AuthContext with parameters "
261                    + "organization name: "
262                    + organizationName
263                    + "; "
264                    + ((principal == null) ? "principal is null"
265                            : "principal: ")
266                    + principal
267                    + "; "
268                    + ((password.length == 0) ? "password is empty\n"
269                            : "password present\n"));
270        }
271        this.startLogin();
272
273        //
274        // assume that there are requirements, and they are NameCallback and
275        // PasswordCallback. then submit those.
276        //
277        while (this.hasMoreRequirements()) {
278            authDebug.message("AuthContext::init() Has requirements");
279            callbacks = this.getRequirements();
280            for (int i = 0; i < callbacks.length; i++) {
281                if (callbacks[i] instanceof NameCallback) {
282                    authDebug.message("Got NameCallback");
283                    NameCallback nc = (NameCallback) callbacks[i];
284                    Set sops = subject.getPrincipals();
285                    AuthSPrincipal[] aps = (AuthSPrincipal[]) sops
286                            .toArray(new AuthSPrincipal[0]);
287                    if (aps.length == 1) {
288                        nc.setName(aps[0].getName());
289                        authDebug.message("Set namecallback name = "
290                                + aps[0].getName());
291                        gotName = true;
292                    }
293                } else if (callbacks[i] instanceof PasswordCallback) {
294                    authDebug.message("Got PasswordCallback");
295                    PasswordCallback pc = (PasswordCallback) callbacks[i];
296                    pc.setPassword(password);
297                    gotPassword = true;
298                } else if (callbacks[i] instanceof TextOutputCallback) {
299                    authDebug.message(
300                            "AuthContext::init() Got TextOutputCallback");
301                } else if (callbacks[i] instanceof TextInputCallback) {
302                    authDebug.message(
303                            "AuthContext::init() Got TextInputCallback");
304                } else if (callbacks[i] instanceof ChoiceCallback) {
305                    authDebug.message("AuthContext::init() Got ChoiceCallback");
306                    ChoiceCallback cc = (ChoiceCallback) callbacks[i];
307                    cc.setSelectedIndex(0);
308                } else {
309                    authDebug.message(
310                            "AuthContext::init() Got Unknown Callback");
311                }
312
313            }
314            this.submitRequiredInformation(callbacks);
315        }
316
317        // Debug messages
318        if (authDebug.messageEnabled() && gotName && gotPassword) {
319            authDebug.message(
320                    "AuthContext::init() Got name and password callbacks");
321        }
322        if (authDebug.messageEnabled()) {
323            authDebug.message("AuthContext::init() Login status: "
324                    + this.getLoginStatus());
325        }
326
327        // Check login status
328        if (getLoginStatus() == AUTH_FAILED) {
329            throw (getLoginException());
330        }
331    }
332
333    /**
334     * Constructor to get an instance of this class given the organization name
335     * <code>orgName</code> the user would like to access, and the principal's
336     * <code>subject</code> the user would like to be authenticated as.
337     */
338    protected AuthContext(String orgName, AuthSubject subject)
339            throws LoginException {
340        String orgname = orgName;
341
342        if (authDebug.messageEnabled()) {
343            authDebug.message("Instantiating AuthContext with parameters "
344                    + "organization name: "
345                    + orgName
346                    + "; "
347                    + ((subject == null) ? "subject is null" : "subject: "
348                            + subject));
349        }
350
351        if (orgName != null) {
352            if (orgName.startsWith("auth://")) {
353                int i2, offset;
354                String subsample;
355                String appName = null;
356
357                offset = 7; // char count of "auth://"
358                subsample = orgName.substring(offset);
359                // the org + appname, supposedly
360                i2 = subsample.indexOf("/");
361                if (i2 != -1) {
362                    //
363                    // from offset to i2 should be the orgName
364                    //
365                    orgname = subsample.substring(0, i2);
366                    authDebug.message("AuthContext::init() auth:// "
367                            + "form, orgname = " + orgname);
368
369                    //
370                    // get past the "/" after the orgName; look for appName
371                    //
372                    subsample = subsample.substring(i2 + 1);
373                    if (subsample.length() > 0) {
374                        //
375                        // the next check could be for a "?", this is for
376                        // possible
377                        // future use where parameters such as
378                        // "?userid=<userid>&password=<pswd>" could be passed
379                        //
380                        i2 = subsample.indexOf("?");
381                        if (i2 != -1) {
382                            //
383                            // parameters specified; pick off appName first
384                            //
385                            appName = subsample.substring(0, i2);
386
387                            //
388                            // the rest assumes the userid and password
389                            // parameters as
390                            // described above. To be implmented
391                            //
392                            // subsample = subsample.substring(i2+1);
393                        } else {
394                            //
395                            // Only appName was provided, no user name and
396                            // password
397                            //
398                            appName = subsample;
399                        }
400                    } else {
401                        //
402                        // no appName, just OrgName and "/" at the end
403                        //
404                        appName = null;
405                    }
406                } else {
407                    //
408                    // means just the orgName was specified
409                    //
410                    orgname = subsample;
411                }
412                if (appName != null) {
413                    applicationName = appName;
414                }
415            } else if (orgName.startsWith("local://")) {
416                authDebug.message("local form AuthContext specified; "
417                        + orgName);
418                int offset = 8; // char count of "local://"
419                orgname = orgName.substring(offset); // just the org,
420                                                        // hopefully
421            }
422        }
423
424        this.organizationName = orgname;
425        reset(subject);
426    }
427
428    // An alternate form of the <code>orgName</code> is
429    // "auth://<orgName>/<appName>"
430    //
431    // note that a private form of orgName is
432    // "local://...". this is for administrative-type
433    // configuration information for install commands,
434    // for example.
435    //
436    /**
437     * Constructor to get an instance of this class
438     * given the organization name <code>orgName</code>. The plug-in modules
439     * would then query for the user name and related information.
440     * 
441     * @param orgName organization name.
442     * @throws LoginException
443     *
444     * @supported.api
445     */
446    public AuthContext(String orgName) throws LoginException {
447        this(orgName, null);
448        authDebug.message("Instantiated AuthContext with organization name: "
449                + orgName);
450    }
451
452    /**
453     * Constructor to re-create a limited instance of this class given the
454     * ByteArray, which was originally obtained using the
455     * <code>toByteArray()</code> method. Using this constructor, the only
456     * methods that will provide valid return values are
457     * <code>getSubject()</code>, <code>getLoginStatus()</code>,
458     * <code>getAuthPrincipal()</code>, and <code>getAuthPrincipals()</code>.
459     */
460    protected AuthContext(byte[] bArray) throws LoginException {
461        try {
462            ByteArrayInputStream bis = new ByteArrayInputStream(bArray);
463            ObjectInputStream bin = new ObjectInputStream(bis);
464
465            String readOrgName = (String) bin.readObject();
466            int readStatus = bin.readInt();
467            AuthSubject readSubject = (AuthSubject) bin.readObject();
468
469            this.organizationName = readOrgName;
470            reset(readSubject);
471            setLoginStatus(readStatus); // change status from starting
472        } catch (IOException e) {
473            authDebug.message("AuthContext::bArray constructor():IOException"
474                    + e);
475            throw (new LoginException(e.getMessage()));
476        } catch (ClassNotFoundException e) {
477            authDebug.message(
478                    "AuthContext::bArray constructor():ClassNotFoundException"
479                            + e);
480            throw (new LoginException(e.getMessage()));
481        }
482    }
483
484    /**
485     * Method to reset this instance of <code>AuthContext</code> object, so
486     * that a new login process can be initiated. Authenticates the user to the
487     * same organization or resource this object was instantiated with. If this
488     * object was instantiated with a <code>
489     * Subject</code>, it will be
490     * ignored.
491     */
492    protected void reset() throws LoginException {
493        authDebug.message("AuthContext::reset()");
494        reset(null);
495        authDebug.message("AuthContext::reset() exiting");
496    }
497
498    /**
499     * Method to reset this instance of <code>AuthContext</code> object, so
500     * that a new login process can be initiated for the given
501     * <code>Subject</code>. Authenticates the user to the same organization
502     * or resource this object was instantiated with.
503     */
504    protected void reset(AuthSubject subject) throws LoginException {
505
506        if (authDebug.messageEnabled()) {
507            authDebug.message("AuthContext::reset(" + organizationName + ", "
508                    + ((subject == null) ? "null" : subject.toString()) + ")");
509        }
510
511        loginStatus = AUTH_NOT_STARTED;
512        informationRequired = null;
513        submittedInformation = null;
514        loginException = null;
515        loginThread = new AuthLoginThread(this);
516        authDebug.message("AuthLoginThread isAlive = " + loginThread.isAlive());
517
518        String contextName = null;
519
520        if (applicationName == null) {
521            contextName = organizationName;
522        } else {
523            contextName = organizationName + "%" + applicationName;
524        }
525
526        authDebug
527                .message("AuthContext::reset:using contextName=" + contextName);
528        if (subject == null) {
529            loginContext = new LoginContext(contextName, loginThread);
530        } else {
531            loginContext = new LoginContext(contextName, subject, loginThread);
532        }
533
534        if (authDebug.messageEnabled()) {
535            authDebug
536                    .message("Successfully reset AuthContext for organization: "
537                            + organizationName
538                            + ((subject == null) ? " with no subject name "
539                                    : " with subjects: " + subject));
540        }
541    }
542
543    /**
544     * Returns the set of Principals the user has been authenticated as. This
545     * can be invoked only after successful authentication. If the
546     * authentication fails, this will return <code>null</code>.
547     */
548    protected AuthSubject getSubject() {
549        authDebug.message("AuthContext::getSubject()");
550        return (loginContext.getSubject());
551    }
552
553    /**
554     * Method to start the login process. This method will
555     * read the plug-ins configured for the application and initialize them.
556     * 
557     * @throws LoginException
558     *
559     * @supported.api
560     */
561    public void startLogin() throws LoginException {
562
563        authDebug.message("AuthContext::startLogin() called");
564
565        // Make sure we are the current state
566        if (getLoginStatus() != AUTH_NOT_STARTED) {
567
568            authDebug.message("AuthContext::startLogin called "
569                    + "when the current login state is" + getLoginStatus());
570
571            throw (new LoginException(myAuthI18n
572                    .getString("authError-invalidMethod" + getLoginStatus())));
573        }
574
575        // Change the login status
576        loginStatus = AUTH_IN_PROGRESS;
577
578        // Initiate the login
579        authDebug.message("AuthContext::startLogin() "
580                + "starting a new thread to run the login process");
581
582        try {
583            loginThread.start();
584        } catch (Exception ex) {
585            authDebug.message("exception starting thread: " + ex);
586            throw (new LoginException(ex.getMessage()));
587        }
588    }
589
590    /**
591     * Returns true if the login process requires more
592     * information from the user to complete the authentication.
593     * 
594     * @return true if the login process requires more information from the user
595     *         to complete the authentication.
596     *
597     * @supported.api
598     */
599    public boolean hasMoreRequirements() {
600        authDebug.message("AuthContext::requiresMoreInformation()");
601
602        if (getRequirements() == null)
603            return (false);
604        else
605            return (true);
606    }
607
608    /**
609     * Returns an array of <code>Callback</code> objects
610     * that must be populated by the user and returned back. These objects are
611     * requested by the authentication plug-ins, and these are usually displayed
612     * to the user. The user then provides the requested information for it to
613     * be authenticated.
614     * 
615     * @return an array of <code>Callback</code> objects that must be
616     *         populated by the user and returned back.
617     *
618     * @supported.api
619     */
620    public Callback[] getRequirements() {
621        authDebug.message("AuthContext::getInformationRequired()");
622
623        // Check the status of LOGIN
624        if (getLoginStatus() != AUTH_IN_PROGRESS) {
625
626            authDebug.message("AuthContext:getInformationRequired() "
627                    + "called when the current login state is: "
628                    + getLoginStatus());
629
630            // Login has completed, could be either success or failure
631            return (null);
632        }
633
634        // Check if information required is present
635        while ((informationRequired == null)
636                && (getLoginStatus() == AUTH_IN_PROGRESS)) {
637            // wait for required information to be available
638            try {
639                authDebug.message("AuthContext::getInformationRequired"
640                        + "() waiting for Callback array");
641
642                synchronized (loginThread) {
643                    if ((informationRequired == null)
644                            && (getLoginStatus() == AUTH_IN_PROGRESS)) {
645                        loginThread.wait();
646                    }
647                }
648
649                authDebug.message("AuthContext::getInformationRequired"
650                        + "() returned from waiting for Callback array");
651
652            } catch (InterruptedException ie) {
653                // do nothing
654            }
655        }
656        return (informationRequired);
657    }
658
659    /**
660     * Submits the populated <code>Callback</code>
661     * objects to the authentication plug-in modules. Called after
662     * <code>getInformationRequired</code> method and obtaining user's
663     * response to these requests.
664     * 
665     * @param info
666     *            array of <code>Callback</code> objects.
667     *
668     * @supported.api
669     */
670    public void submitRequiredInformation(Callback[] info) {
671        authDebug.message("AuthContext::submitRequestedInformation()");
672
673        informationRequired = null;
674
675        // Set the submitted info & wake up the callback hander thread
676        synchronized (loginThread) {
677            submittedInformation = info;
678            loginThread.notify();
679        }
680        authDebug.message("AuthContext::submitRequestedInformation"
681                + "() sending notify to sleeping threads");
682    }
683
684    /**
685     * Logs the user out.
686     * 
687     * @throws LoginException
688     *
689     * @supported.api
690     */
691    public void logout() throws LoginException {
692        authDebug.message("AuthContext::logout()");
693        loginContext.logout();
694
695        authDebug.message("Called LoginContext::logout()");
696        loginStatus = AUTH_COMPLETED;
697    }
698
699    /**
700     * Returns login exception, if any, during the
701     * authentication process. Typically set when the login fails.
702     * 
703     * @return login exception.
704     *
705     * @supported.api
706     */
707    public LoginException getLoginException() {
708        authDebug.message("AuthContext::getLoginException()");
709        return (loginException);
710    }
711
712    /**
713     * Returns the current state of the login process.
714     * Possible states are listed above.
715     * 
716     * @return the current state of the login process.
717     *
718     * @supported.api
719     */
720    public int getLoginStatus() {
721        authDebug.message("AuthContext::getLoginStatus()");
722        return (loginStatus);
723    }
724
725    /**
726     * Method to set the login status. Used internally and not visible outside
727     * this package.
728     */
729    protected void setLoginStatus(int status) {
730        authDebug.message("AuthContext::setLoginStatus()");
731        loginStatus = status;
732    }
733
734    /**
735     * Returns the (first) <code>AuthPrincipal</code> in
736     * the <code>Subject</code>. Returns the first <code>Principal</code>,
737     * if more than one exists.
738     * 
739     * @return the (first) <code>AuthPrincipal</code> in the
740     *         <code>Subject</code>.
741     *
742     * @supported.api
743     */
744    public Principal getPrincipal() {
745        Set sop = getSubject().getPrincipals();
746        if (authDebug.messageEnabled()) {
747            authDebug.message("AuthContext::getAuthPrincipal(): " + sop);
748        }
749        Iterator items = sop.iterator();
750        if (items.hasNext()) {
751            return ((Principal) items.next());
752        } else {
753            return null;
754        }
755    }
756
757    /**
758     * Method to get the (first) <code>AuthPrincipal</code> in the
759     * <code>Subject</code>. Returns the first <code>Principal</code>, if
760     * more than one exists.
761     * 
762     * @deprecated Use getPrincipal() instead
763     */
764    public AuthPrincipal getAuthPrincipal() {
765        authDebug.message("AuthContext::getAuthPrincipal()");
766
767        Set sop = getSubject().getPrincipals();
768        Iterator items = sop.iterator();
769        if (items.hasNext())
770            return ((AuthPrincipal) items.next());
771        else
772            return null;
773    }
774
775    /**
776     * Method to get the set of <code>AuthPrincipal</code>s in the
777     * <code>Subject</code>.
778     */
779    protected Set getPrincipals() {
780        authDebug.message("AuthContext::getAuthPrincipals()");
781        return (getSubject().getPrincipals());
782    }
783
784    /**
785     * Method to retrieve a Byte array of serializable portions of the
786     * <code>AuthContext</code>.
787     */
788    protected byte[] toByteArray() {
789        try {
790            ByteArrayOutputStream bos = new ByteArrayOutputStream();
791            ObjectOutputStream bout = new ObjectOutputStream(bos);
792
793            bout.writeObject(((organizationName == null) ? " "
794                    : organizationName));
795            bout.writeInt(loginStatus);
796            bout.writeObject(loginContext.getSubject());
797            byte[] bytestuff = bos.toByteArray();
798            return (bytestuff);
799        } catch (IOException iox) {
800            if (authDebug.messageEnabled()) {
801                authDebug.message("AuthContext:toByteArray():IOException", iox);
802            }
803        } catch (Exception e) {
804            if (authDebug.messageEnabled()) {
805                authDebug.message("AuthContext:toByteArray():Exception", e);
806            }
807        }
808        return null;
809    }
810
811    /**
812     * Method to get organization name that was set during
813     * construction of this instance.
814     * 
815     * @return organization name; <code>null</code> if it was not initialized
816     *         during construction of this instance
817     *
818     * @supported.api
819     */
820    public String getOrganizationName() {
821        if (organizationName == null) {
822            String rootSuffix = organizationName = ServiceManager.getBaseDN();
823            if ((rootSuffix != null) && (organizationName != null)) {
824                rootSuffix = new DN(rootSuffix).toRFCString().toLowerCase();
825                organizationName = new DN(organizationName).toRFCString()
826                        .toLowerCase();
827            }
828        }
829        return organizationName;
830    }
831
832    protected String getApplicationName() {
833        return applicationName;
834    }
835
836    /**
837     * Method to get the Single-Sign-On (SSO) Token. This
838     * token can be used as the authenticated token.
839     * 
840     * @return single-sign-on token.
841     * @throws InvalidAuthContextException
842     *
843     * @supported.api
844     */
845    public SSOToken getSSOToken() throws InvalidAuthContextException {
846        if (token != null) {
847            return (token);
848        }
849
850        token = new AuthSSOToken(this);
851        try {
852            // Set Organization
853            if (getOrganizationName() != null) {
854                token.setProperty(Constants.ORGANIZATION,
855                    getOrganizationName());
856            }
857
858            // Set Host name
859            InetAddress address = InetAddress.getLocalHost();
860            String ipAddress = address.getHostAddress();
861            String strHostName = address.getHostName();
862
863            if (authDebug.messageEnabled()) {
864                authDebug.message("Complete Host : " + address.toString());
865                authDebug.message("getSSOToken : HOST Name : " + strHostName);
866                authDebug.message("getSSOToken : IP : " + ipAddress);
867            }
868
869            if (ipAddress != null) {
870                if (isEnableHostLookUp) {
871                    if (strHostName != null) {
872                        token.setProperty("HostName", strHostName);
873                    }
874                } else {
875                    token.setProperty("HostName", ipAddress);
876                }
877                token.setProperty("Host", ipAddress);
878            }
879
880            // Set AuthType
881            token.setProperty("AuthType", "ldap");
882
883            // Set Principal
884             String principal = getPrincipal().getName();
885             if (principal != null) {
886                 token.setProperty("Principal", principal);
887                 // Set Universal Identifier
888                 String username = principal;
889                 if (DN.isDN(principal)) {
890                     // Get the username
891                     username = LDAPDN.explodeDN(principal, true)[0];
892                 }
893                 // Since internal auth will be used during install time
894                 // and during boot strap for users "dsame" and "amadmin"
895                 // the IdType will be hardcoded to User
896                 StringBuilder uuid = new StringBuilder(100);
897                 uuid.append("id=").append(username)
898                 .append(",ou=user,").append(getOrganizationName());
899                 token.setProperty(Constants.UNIVERSAL_IDENTIFIER,
900                     uuid.toString());
901             }
902
903            // Set AuthLevel
904            token.setProperty("AuthLevel", Integer.toString(0));
905
906            //Set ContextId 
907            SecureRandom secureRandom = 
908                SecureRandomManager.getSecureRandom();
909            String amCtxId = 
910                Long.toHexString(secureRandom.nextLong());
911            token.setProperty(Constants.AM_CTX_ID, amCtxId);
912
913            if (authDebug.messageEnabled()) {
914                authDebug.message("SSOToken : Organization : "
915                        + token.getProperty("Organization"));
916                authDebug.message("SSOToken : Principal : "
917                        + token.getProperty("Principal"));
918                authDebug.message("SSOToken : HostName : "
919                        + token.getProperty("HostName"));
920                authDebug.message("SSOToken : Host : "
921                        + token.getProperty("Host"));
922                authDebug.message("SSOToken : getIPAddress : "
923                        + token.getIPAddress());
924                authDebug.message("SSOToken : getHostName : "
925                        + token.getHostName());
926                authDebug.message("SSOToken : ContextId : "
927                        + token.getProperty(Constants.AM_CTX_ID));
928            }
929
930        } catch (Exception e) {
931            if (authDebug.warningEnabled()) {
932                authDebug.warning("getSSOToken: setProperty exception : ", e);
933            }
934        }
935
936        return (token);
937    }
938}