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: SSOProviderImpl.java,v 1.9 2009/02/19 05:04:01 bhavnab Exp $
026 *
027 */
028
029/**
030 * Portions Copyrighted 2013 ForgeRock, Inc.
031 */
032package com.iplanet.sso.providers.dpro;
033
034import com.iplanet.am.util.SystemProperties;
035import com.iplanet.dpro.session.Session;
036import com.iplanet.dpro.session.SessionException;
037import com.iplanet.dpro.session.SessionID;
038import com.iplanet.sso.SSOException;
039import com.iplanet.sso.SSOProvider;
040import com.iplanet.sso.SSOToken;
041import com.iplanet.sso.SSOTokenID;
042import com.sun.identity.common.SearchResults;
043import com.sun.identity.shared.debug.Debug;
044import org.forgerock.openam.utils.ClientUtils;
045import java.net.InetAddress;
046import java.util.HashSet;
047import java.util.Iterator;
048import java.util.Set;
049import javax.servlet.http.HttpServletRequest;
050
051/**
052 * This <code>final</code> class <code>SSOProviderImpl</code> implements 
053 * <code>SSOProvider</code> interface and provides implementation of the methods
054 * to create , destroy , check the validity of a single sign on token.
055 *
056 * @supported.api
057 */
058public final class SSOProviderImpl implements SSOProvider {
059
060    /** Debug SSOProvider */
061    public static Debug debug = null;
062
063    /** Check to see if the clientIPCheck is enabled */
064    private static boolean checkIP = Boolean.valueOf(
065            SystemProperties.get("com.iplanet.am.clientIPCheckEnabled"))
066            .booleanValue();
067
068    // Initialize debug instance;
069    static {
070        debug = Debug.getInstance("amSSOProvider");
071    }
072
073    /**
074     * Constructs a instance of <code>SSOProviderImpl</code>
075     * 
076     * @throws SSOException
077     *
078     * @supported.api
079     */
080    public SSOProviderImpl() throws SSOException {
081    }
082
083    /**
084     * Creates a single sign on token for the <code>HttpRequest</code>
085     *
086     * @param request <code>HttpServletRequest</code>
087     * @return single sign on token for the request
088     * @exception SSOException if the single sign on token cannot be created.
089     */
090    public SSOToken createSSOToken(HttpServletRequest request)
091            throws SSOException {
092        try {
093            SessionID sid = new SessionID(request);
094            Session session = Session.getSession(sid);
095            if (sid != null) {
096                Boolean cookieMode = sid.getCookieMode();
097                if (debug.messageEnabled()) {
098                    debug.message("cookieMode is :" + cookieMode);
099                }
100                if (cookieMode != null) {
101                    session.setCookieMode(cookieMode);
102                }
103            }
104            if (checkIP && !isIPValid(session, ClientUtils.getClientIPAddress(request))) {
105                throw new Exception(SSOProviderBundle.getString("invalidIP"));
106            }
107            SSOToken ssoToken = new SSOTokenImpl(session);
108            return ssoToken;
109        } catch (Exception e) {
110            if (debug.messageEnabled()) {
111                debug.message("could not create SSOToken from HttpRequest", e);
112            }
113            throw new SSOException(e);
114        }
115    }
116
117    /** Creates a single sign on token with user or service as the entity
118     * 
119     * @param user Principal representing a user or service
120     * @param password password string.
121     * @return single sign on token
122     * @exception SSOException if the single sign on token cannot be created.
123     * @exception UnsupportedOperationException Thrown to indicate that the 
124     * requested operation is not supported.
125     * @deprecated This method has been deprecated. Please use the
126     * regular LDAP authentication mechanism instead. More information 
127     * on how to use the authentication programming interfaces as well as the
128     *             code samples can be obtained from the "Authentication
129     *             Service" chapter of the OpenSSO Developer's Guide.
130     */
131    public SSOToken createSSOToken(java.security.Principal user,
132            String password) throws SSOException, UnsupportedOperationException 
133    {
134        try {
135            SSOTokenImpl ssoToken = new SSOTokenImpl(user, password);
136            if (debug.messageEnabled()) {
137                debug.message("SSO token ldap auth successful for "
138                        + user.toString());
139            }
140            return ssoToken;
141        } catch (Exception e) {
142            if (debug.messageEnabled()) {
143                debug.message("could not create SSOTOken for user "
144                        + user.getName(), e);
145            }
146            throw new SSOException(e);
147        }
148    }
149
150    /**
151     * Creates a single sign on token. Note: this method should remain private
152     * and get called only by the AuthContext API.
153     * 
154     * @param tokenId single sign on token ID.
155     * @param invokedByAuth boolean flag indicating that this method has
156     *        been invoked by the AuthContext.getSSOToken() API.
157     * @return single sign on token.
158     * @exception SSOException if the single sign on token cannot be created.
159     * @exception UnsupportedOperationException Thrown to indicate that the 
160     * requested operation is not supported.
161     */
162    public SSOToken createSSOToken(String tokenId, boolean invokedByAuth) 
163            throws SSOException, UnsupportedOperationException 
164    {
165        try {
166            SessionID sessionId = new SessionID(tokenId);
167            sessionId.setComingFromAuth(invokedByAuth);
168            Session session = Session.getSession(sessionId);
169            SSOToken ssoToken = new SSOTokenImpl(session);
170            return ssoToken;
171        } catch (Exception e) {
172            if (debug.messageEnabled()) {
173                debug.message("SSOProviderImpl.createSSOToken(tokenId, boolean)"
174                    +"could not create SSOTOken for token ID " + tokenId, e);
175            }
176            throw new SSOException(e);
177        }
178    }
179
180     /**
181      * Creates a single sign on token.
182      *
183      * @param tokenId single sign on token ID.
184      * @return single sign on token.
185      * @exception SSOException if the single sign on token cannot be created.
186      * @exception UnsupportedOperationException
187      * @deprecated Use #createSSOToken(String, String)
188      */
189     public SSOToken createSSOToken(String tokenId)
190                                    throws SSOException,
191                                    UnsupportedOperationException {
192         return createSSOToken(tokenId, false);
193     }
194
195    /**
196     * Creates a single sign on token.
197     * 
198     * @param tokenId
199     *            single sign on token ID.
200     * @param clientIP client IP address
201     * @return single sign on token.
202     * @exception SSOException if the single sign on token cannot be created.
203     * @exception UnsupportedOperationException Thrown to indicate that the 
204     * requested operation is not supported.
205     * @deprecated Use #createSSOToken(String, String)
206     */
207    public SSOToken createSSOToken(String tokenId, String clientIP)
208            throws SSOException, UnsupportedOperationException {
209        try {
210            SessionID sessionId = new SessionID(tokenId);
211            Session session = Session.getSession(sessionId);
212            if (checkIP && !isIPValid(session, clientIP)) {
213                throw new Exception(SSOProviderBundle.getString("invalidIP"));
214            }
215            SSOToken ssoToken = new SSOTokenImpl(session);
216            return ssoToken;
217        } catch (Exception e) {
218            if (debug.messageEnabled()) {
219                debug.message("could not create SSOTOken for token ID "
220                        + tokenId, e);
221            }
222            throw new SSOException(e);
223        }
224    }
225
226   /**
227    * Checks the validity of the single sign on token
228    * @param token The single sign on token object to be validated
229    * @return Returns true if the <code>SSOToken</code> is valid
230    */
231    public boolean isValidToken(SSOToken token) {
232        /*
233         * if the token was created from createSSOToken(Principal, password)
234         * there is no association with session. Use this temp solution now. if
235         * this method is going to go away, we can remove that method. otheriwse
236         * a better mechanism has to be implemented.
237         */
238        SSOTokenImpl tokenImpl = (SSOTokenImpl) token;
239        return (tokenImpl.isValid());
240    }
241
242    /**
243     * Checks if the single sign on token is valid.
244     * 
245     * @param token
246     *            single sign on token.
247     * @exception SSOException
248     *                if the single sign on token is not valid.
249     */
250    public void validateToken(SSOToken token) throws SSOException {
251        try {
252            /*
253             * if the token was created from createSSOToken(Principal, password)
254             * there is no association with session. Use this temp solution now.
255             * if this method is going to go away, we can remove that method.
256             * otheriwse a better mechanism has to be implemented.
257             */
258            SSOTokenImpl tokenImpl = (SSOTokenImpl) token;
259            tokenImpl.validate();
260        } catch (Exception e) {
261            if (debug.messageEnabled()) {
262                debug.message("validateToken: ", e);
263            }
264            throw new SSOException(SSOProviderBundle.rbName, "invalidtoken",
265                    null);
266        }
267    }
268
269    /** Destroys a single sign on token
270     *
271     * @param token The single sign on token object to be destroyed
272     * @throws SSOException if the given token cannot be destroyed
273     */
274    public void destroyToken(SSOToken token) throws SSOException {
275        try {
276            SSOTokenImpl tokenImpl = (SSOTokenImpl) token;
277            if (tokenImpl.isLdapConnection() == true) {
278                tokenImpl.setStatus(false);
279                return;
280            }
281            SSOTokenID tokenid = token.getTokenID();
282            String id = tokenid.toString();
283            SessionID sessid = new SessionID(id);
284            Session session = Session.getSession(sessid);
285            session.destroySession(session);
286        } catch (Exception e) {
287            if (debug.messageEnabled()) {
288                debug.message("DestroyToken: ", e);
289            }
290            throw new SSOException(e);
291        }
292    }
293
294     /**
295      * Validate the IP address of the client with the IP stored in Session.
296      * @param sess Session object associated with the token
297      * @param clientIP IP address of the current client who made 
298      * <code>HttpRequest</code>.
299      * @return Returns true if the IP is valid else false.
300      * @throws SSOException if IP cannot be validated for the given session
301      */
302    public boolean isIPValid(Session sess, String clientIP) throws SSOException
303    {
304        boolean check = false;
305        try {
306            InetAddress sessIPAddress = InetAddress.getByName(sess
307                    .getProperty("Host"));
308            InetAddress clientIPAddress = InetAddress.getByName(clientIP);
309            if (sessIPAddress.equals(clientIPAddress)) {
310                check = true;
311            }
312        } catch (Exception e) {
313            if (debug.messageEnabled()) {
314                debug.message("IP address check of Token Failed", e);
315            }
316        }
317        return check;
318    }
319
320    /**
321     * Refresh the Session corresponding to the single sign on token from the
322     * Session Server.
323     * @param token single sign on token for which session need to be refreshed
324     * @throws SSOException if the session cannot be refreshed
325     * 
326     */
327    public void refreshSession(SSOToken token) throws SSOException {
328        try {
329            SSOTokenID tokenId = token.getTokenID();
330            SessionID sid = new SessionID(tokenId.toString());
331            Session session = Session.getSession(sid);
332            session.refresh(true);
333        } catch (Exception e) {
334            debug.error("Error in refreshing the session from sessions erver");
335            throw new SSOException(e);
336        }
337    }
338
339    /**
340     * Destroys a single sign on token.
341     * 
342     * @param destroyer
343     *            The single sign on token object used to authorize the
344     *            operation
345     * @param destroyed
346     *            The single sign on token object to be destroyed.
347     * @throws SSOException
348     *             if the there was an error during communication with session
349     *             service.
350     *
351     * @supported.api
352     */
353    public void destroyToken(SSOToken destroyer, SSOToken destroyed)
354            throws SSOException {
355        try {
356            Session requester = ((SSOTokenImpl) destroyer).getSession();
357            Session target = ((SSOTokenImpl) destroyed).getSession();
358            requester.destroySession(target);
359        } catch (SessionException e) {
360            throw new SSOException(e);
361        }
362    }
363
364    /**
365     * Returns a list of single sign on token objects
366     * which correspond to valid Sessions accessible to requester. single sign
367     * on token objects returned are restricted: they can only be used to
368     * retrieve properties and destroy sessions they represent.
369     * 
370     * @param requester
371     *            The single sign on token object used to authorize the
372     *            operation
373     * @param server
374     *            The server for which the valid sessions are to be retrieved
375     * @return Set of Valid Sessions
376     * @throws SSOException
377     *             if the there was an error during communication with session
378     *             service.
379     *
380     * @supported.api
381     */
382    public Set getValidSessions(SSOToken requester, String server)
383            throws SSOException {
384        Set results = new HashSet();
385        try {
386
387            SearchResults result = ((SSOTokenImpl) requester).getSession()
388                    .getValidSessions(server, null);
389
390            for (Iterator iter = result.getResultAttributes().values()
391                    .iterator(); iter.hasNext();) {
392                Session s = (Session) iter.next();
393                if (s != null) {
394                    results.add(new SSOTokenImpl(s));
395                }
396            }
397        } catch (SessionException e) {
398            throw new SSOException(e);
399        }
400        return results;
401    }
402}