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: SSOTokenManager.java,v 1.7 2009/02/18 23:59:36 qcheng Exp $
026 *
027 */
028
029package com.iplanet.sso;
030
031import com.iplanet.services.util.I18n;
032import com.iplanet.sso.providers.dpro.SSOProviderBundle;
033import com.iplanet.ums.IUMSConstants;
034import com.sun.identity.shared.debug.Debug;
035import java.security.Principal;
036import java.util.Set;
037
038/**
039 * SSOTokenManager is the final class that is the mediator between the SSO APIs
040 * and SSO providers. When an SSO client makes an API invocation,
041 * SSOTokenManager will delegate that call to the SSO provider/plug-in. The SSO
042 * provider will execute the call and return the results to SSOTokenManager,
043 * which in turn returns the results to the SSO client. This decouples the SSO
044 * clients from the actual SSO providers. You should be able to replace the SSO
045 * provider without having to modify the SSO client. However, the clients can
046 * invoke the class methods on the objects returned by the SSOTokenManager. 
047 * <p>
048 * SSOTokenManager is a singleton class; there can be, at most, only one
049 * instance of SSOTokenManager in any given JVM. <p> SSOTokenManager currently
050 * supports only two kinds of provider, one for Grappa and another for Sun
051 * OpenSSO. In the future, this will be extended to support 
052 * <p> It is assumed that the provider classes or the JAR file is in the 
053 * CLASSPATH so that they can be found automatically. Providers can be 
054 * configured using <code>providerimplclass</code> property. 
055 * This property must be set to the complete (absolute) package name of the 
056 * main class of the provider. For example, if the provider class is
057 * com.iplanet.sso.providers.dpro.SSOProviderImpl, that entire class name
058 * including package prefixes MUST be specified. The main class MUST implement
059 * the com.iplanet.sso.SSOProvider interface and MUST have a public no-arg
060 * default constructor.
061 * <p>
062 * The class <code>SSOTokenManager</code> is a <code>final</code> class that
063 * provides interfaces to create and validate <code>SSOToken</code>s.
064 * <p>
065 * It is a singleton class; an instance of this class can be obtained by calling
066 * <code>SSOTokenManager.getInstance()</code>.
067 * <p>
068 * Having obtained an instance of <code>SSOTokenManager</code>, its methods
069 * can be called to create <code>SSOToken</code>, get <code>SSOToken</code>
070 * given the <code>SSOTokenID</code> in string format, and to validate
071 * <code>SSOToken</code>s.
072 *
073 * @supported.api
074 */
075public class SSOTokenManager {
076    
077    /*
078     * SSOTokenManager is not a real provider but implements SSOProvider for
079     * consistency in the methods.
080     */
081
082    /**
083     * Grappa SSOProvider class that will be used by default if
084     * providerimplclass property is not present.
085     */
086    static final String GRAPPA_PROVIDER_PACKAGE = 
087        "com.sun.identity.authentication.internal";
088
089    static SSOProvider grappaProvider = null;
090
091    /**
092     * DPRO SSOProvider class
093     */
094    static SSOProvider dProProvider = null;
095
096    /** Debug class that can be used by SSOProvider implementations */
097    public static Debug debug = Debug.getInstance(IUMSConstants.UMS_DEBUG);
098
099    // Singleton instance of SSOTokenManager
100    private static SSOTokenManager instance = null;
101
102    /**
103     * Returns the singleton instance of
104     * <code>SSOTokenManager</code>.
105     * 
106     * @return The singleton <code>SSOTokenManager</code> instance
107     * @throws SSOException
108     *             if unable to get the singleton <code>SSOTokenManager</code>
109     *             instance.
110     * @supported.api
111     */
112    public static SSOTokenManager getInstance() throws SSOException {
113
114        /*
115         * We will use the double-checked locking pattern. Rarely entered block.
116         * Push synchronization inside it. This is the first check.
117         */
118
119        if (instance == null) {
120            /*
121             * Only 1 thread at a time gets past the next point. Rarely executed
122             * synchronization statement and hence synchronization penalty is
123             * not paid every time this method is called.
124             */
125
126            synchronized (SSOTokenManager.class) {
127                /*
128                 * If a second thread was waiting to get here, it will now find
129                 * that the instance has already been initialized, and it will
130                 * not re-initialize the instance variable. This is the
131                 * double-check.
132                 */
133
134                if (instance == null) {
135                    /*
136                     * Here is the critical section that lazy initializes the
137                     * singleton variable.
138                     */
139                    debug.message(
140                            "Constructing a new instance of SSOTokenManager");
141                    instance = new SSOTokenManager();
142                }
143            }
144        }
145        return (instance);
146    }
147
148    /**
149     * Since this class is a singleton, the constructor is suppressed. This
150     * constructor will try to find the provider jar files, load them, then find
151     * the provider mainclass, instantiate it and store it in provider.
152     * Providers can be configured using <code>providerimplclass</code> Java
153     * property. This property must be set to the complete (absolute) package
154     * name of the main class of the provider. The main class MUST implement the
155     * com.iplanet.sso.SSOProvider interface and MUST have a public no-arg
156     * default constructor.
157     */
158    private SSOTokenManager() throws SSOException {
159        Throwable dProException = null;
160        // Obtain the Grappa provider class
161        try {
162            grappaProvider = new 
163                com.sun.identity.authentication.internal.AuthSSOProvider();
164            if (debug.messageEnabled()) {
165                debug.message("Obtained Grappa SSO Provider");
166            }
167        } catch (Throwable e) {
168            debug.error("Unable to obtain Grappa SSO provider", e);
169            dProException = e;
170        }
171
172        // Obtain the DPRO provide class
173        try {
174            dProProvider = new com.iplanet.sso.providers.dpro.SSOProviderImpl();
175            if (debug.messageEnabled()) {
176                debug.message("Obtained DPRO SSO Provider");
177            }
178        } catch (Throwable e) {
179            debug.error("DPRO SSO Provider Exception", e);
180            dProException = e;
181        }
182
183        if (dProProvider == null && grappaProvider == null) {
184            debug.error("Unable to obtain either GRAPPA or DPRO SSO providers");
185            I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG);
186            String rbName = i18n.getResBundleName();
187            if (dProException instanceof ClassNotFoundException)
188                throw new SSOException(rbName,
189                        IUMSConstants.SSO_NOPROVIDERCLASS, null);
190            else if (dProException instanceof InstantiationException)
191                throw new SSOException(rbName,
192                        IUMSConstants.SSO_NOPROVIDERINSTANCE, null);
193            else if (dProException instanceof IllegalAccessException)
194                throw new SSOException(rbName, IUMSConstants.SSO_ILLEGALACCESS,
195                        null);
196            else
197                throw new SSOException(dProException);
198        }
199    }
200
201    /**
202     * Get provider based on SSOToken provided
203     * @param token Single signon SSOToken
204     * @exception SSOException in case of erros when getting the provider
205     */
206    protected static SSOProvider getProvider(SSOToken token)
207            throws SSOException {
208        if (token == null) {
209            throw new SSOException(SSOProviderBundle.rbName, "ssotokennull",
210                    null);
211        }
212        String packageName = token.getClass().getName();
213        if (packageName.startsWith(GRAPPA_PROVIDER_PACKAGE)) {
214            if (grappaProvider == null) {
215                I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG);
216                throw new SSOException(i18n.getResBundleName(),
217                        IUMSConstants.SSO_NOPROVIDERCLASS, null);
218            }
219            return (grappaProvider);
220        }
221        return (dProProvider);
222    }
223
224    /**
225     * Creates a single sign on token from <code>HttpServletRequest</code>
226     * 
227     * @param request
228     *            The <code>HttpServletRequest</code> object which contains
229     *            the session string.
230     * @return single sign on <code>SSOToken</code>
231     * @exception SSOException
232     *                if the single sign on token cannot be created.
233     * @exception UnsupportedOperationException
234     *                if this is an unsupported operation.
235     * @supported.api
236     */
237    public SSOToken createSSOToken(
238            javax.servlet.http.HttpServletRequest request)
239            throws UnsupportedOperationException, SSOException {
240        if (dProProvider != null)
241            return (dProProvider.createSSOToken(request));
242        else
243            return (grappaProvider.createSSOToken(request));
244    }
245
246    /**
247     * Creates a single sign on token after authenticating
248     * the principal with the given password. This method of creating a single
249     * sign on token should only be used for command line applications and it is
250     * forbidden to use this single sign on token in any other context (e.g.
251     * policy, federation, etc.). A token created with this method is only valid
252     * within the context of the calling application. Once the process exits the
253     * token will be destroyed. If token is created using this constructor then
254     * ONLY these methods of single sign on token is supported -
255     * 
256     * <pre>
257     *  getAuthType(), 
258     *  getHostName(), 
259     *  getIPAddress(), 
260     *  setProperty(String name, String value), 
261     *  getProperty(String name), 
262     *  isValid(), 
263     *  validate(). 
264     * </pre>
265     * 
266     * @param user
267     *            Principal representing a user or service
268     * @param password
269     *            The password supplied for the principal
270     * @return single sign on token
271     * @exception SSOException
272     *                if the single sign on token cannot be created.
273     * @exception UnsupportedOperationException
274     *                if this is an unsupported operation.
275     * @deprecated This method has been deprecated. Please use the regular LDAP
276     *             authentication mechanism instead. More information on how to
277     *             use the authentication programming interfaces as well as the
278     *             code samples can be obtained from the "Authentication
279     *             Service" chapter of the OpenSSO Developer's Guide.
280     */
281    public SSOToken createSSOToken(Principal user, String password)
282            throws UnsupportedOperationException, SSOException {
283        if (dProProvider != null)
284            return (dProProvider.createSSOToken(user, password));
285        else
286            return (grappaProvider.createSSOToken(user, password));
287    }
288
289    /**
290     * Creates a single sign on token from the single sign
291     * on token ID. Note:-If you want to do Client's IP address validation for
292     * the single sign on token then use
293     * <code>creatSSOToken(String, String)</code> OR
294     * <code>createSSOToken(HttpServletRequest)</code>.
295     * 
296     * @param tokenId
297     *            Token ID of the single sign on token
298     * @return single sign on token
299     * @exception SSOException
300     *                if the single sign on token cannot be created.
301     * @exception UnsupportedOperationException
302     * @supported.api
303     */
304    public SSOToken createSSOToken(String tokenId)
305            throws UnsupportedOperationException, SSOException {
306        if (dProProvider != null)
307            return (dProProvider.createSSOToken(tokenId));
308        else
309            return (grappaProvider.createSSOToken(tokenId));
310    }
311
312    /**
313     * Creates a single sign on token from the single sign
314     * on token ID.
315     * 
316     * @param tokenId
317     *            Token ID of the single sign on token
318     * @param clientIP
319     *            Client IP address. This must be the IP address of the
320     *            client/user who is accessing the application.
321     * @return single sign on token
322     * @exception SSOException
323     *                if the single sign on token cannot be created.
324     * @exception UnsupportedOperationException
325     * @supported.api
326     */
327    public SSOToken createSSOToken(String tokenId, String clientIP)
328            throws UnsupportedOperationException, SSOException {
329        if (dProProvider != null)
330            return (dProProvider.createSSOToken(tokenId, clientIP));
331        else
332            return (grappaProvider.createSSOToken(tokenId, clientIP));
333    }
334
335    /**
336     * Returns true if a single sign on token is valid.
337     * 
338     * @param token
339     *            The single sign on token object to be validated.
340     * @return true if the single sign on token is valid.
341     * @supported.api
342     */
343    public boolean isValidToken(SSOToken token) {
344        try {
345            return (getProvider(token).isValidToken(token));
346        } catch (SSOException ssoe) {
347            return (false);
348        }
349    }
350
351    /**
352     * Returns true if the single sign on token is valid.
353     * 
354     * @param token
355     *            The single sign on token object to be validated.
356     * @exception SSOException
357     *                if the single sign on token is not valid.
358     * @supported.api
359     */
360    public void validateToken(SSOToken token) throws SSOException {
361        getProvider(token).validateToken(token);
362    }
363
364    /**
365     * Destroys a single sign on token.
366     * 
367     * @param token
368     *            The single sign on token object to be destroyed.
369     * @exception SSOException
370     *                if there was an error while destroying the token, or the
371     *                corresponding session reached its maximum session/idle
372     *                time, or the session was destroyed.
373     * @supported.api
374     */
375    public void destroyToken(SSOToken token) throws SSOException {
376        getProvider(token).destroyToken(token);
377    }
378
379    /**
380     * Refresh the Session corresponding to the single
381     * sign on token from the Session Server. This method should only be used
382     * when the client cannot wait the "session cache interval" for updates on
383     * any changes made to the session properties in the session server. If the
384     * client is remote, calling this method results in an over the wire request
385     * to the session server.
386     * 
387     * @param token
388     *            single sign on token
389     * @exception SSOException
390     *                if the session reached its maximum session time, or the
391     *                session was destroyed, or there was an error while
392     *                refreshing the session.
393     * @supported.api
394     */
395    public void refreshSession(SSOToken token) throws SSOException {
396        try {
397            getProvider(token).refreshSession(token);
398        } catch (Exception e) {
399            debug.error("Error in refreshing the session from session server");
400            throw new SSOException(e);
401        }
402    }
403
404    /**
405     * Destroys a single sign on token.
406     * 
407     * @param destroyer
408     *            The single sign on token object used to authorize the
409     *            operation
410     * @param destroyed
411     *            The single sign on token object to be destroyed.
412     * @throws SSOException
413     *             if the there was an error during communication with session
414     *             service.
415     * @supported.api
416     */
417    public void destroyToken(SSOToken destroyer, SSOToken destroyed)
418            throws SSOException {
419        getProvider(destroyer).destroyToken(destroyer, destroyed);
420    }
421
422    /**
423     * Returns a list of single sign on token objects
424     * which correspond to valid Sessions accessible to requester. Single sign
425     * on tokens returned are restricted: they can only be used to retrieve
426     * properties and destroy sessions they represent.
427     * 
428     * @param requester
429     *            The single sign on token object used to authorize the
430     *            operation
431     * @param server
432     *            The server for which the valid sessions are to be retrieved
433     * @return Set The set of single sign on tokens representing valid Sessions.
434     * @throws SSOException
435     *             if the there was an error during communication with session
436     *             service.
437     * @supported.api
438     */
439    public Set getValidSessions(SSOToken requester, String server)
440            throws SSOException {
441        return getProvider(requester).getValidSessions(requester, server);
442    }
443}