001: /*
002: *
003: * ====================================================================
004: * The Apache Software License, Version 1.1
005: *
006: * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
007: * reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowlegement:
023: * "This product includes software developed by the
024: * Apache Software Foundation (http://www.apache.org/)."
025: * Alternately, this acknowlegement may appear in the software itself,
026: * if and wherever such third-party acknowlegements normally appear.
027: *
028: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
029: * Foundation" must not be used to endorse or promote products derived
030: * from this software without prior written permission. For written
031: * permission, please contact apache@apache.org.
032: *
033: * 5. Products derived from this software may not be called "Apache"
034: * nor may "Apache" appear in their names without prior written
035: * permission of the Apache Group.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
041: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
042: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
043: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
044: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
045: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
046: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
047: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
048: * SUCH DAMAGE.
049: * ====================================================================
050: *
051: * This software consists of voluntary contributions made by many
052: * individuals on behalf of the Apache Software Foundation. For more
053: * information on the Apache Software Foundation, please see
054: * <http://www.apache.org/>.
055: *
056: * [Additional notices, if required by prior licensing conditions]
057: *
058: */
059:
060: package com.technoetic.xplanner.security.module.jndi;
061:
062: import java.util.Map;
063: import javax.security.auth.Subject;
064: import javax.servlet.http.HttpServletRequest;
065:
066: import org.apache.commons.beanutils.BeanUtils;
067: import org.apache.log4j.Logger;
068:
069: import com.technoetic.xplanner.security.LoginModule;
070: import com.technoetic.xplanner.security.AuthenticationException;
071: import com.technoetic.xplanner.security.module.LoginSupport;
072: import com.technoetic.xplanner.security.module.LoginSupportImpl;
073:
074: import com.sabre.security.jndi.JNDIAuthenticator;
075: import com.sabre.security.jndi.JNDIAuthenticatorImpl;
076:
077: /**
078: * <p>Implementation of <strong>Realm</strong> that works with a directory
079: * server accessed via the Java Naming and Directory Interface (JNDI) APIs.
080: * The following constraints are imposed on the data structure in the
081: * underlying directory server:</p>
082: * <ul>
083: * <p/>
084: * <li>Each user that can be authenticated is represented by an individual
085: * element in the top level <code>DirContext</code> that is accessed
086: * via the <code>connectionURL</code> property.</li>
087: * <p/>
088: * <li>If a socket connection can not be made to the <code>connectURL</code>
089: * an attempt will be made to use the <code>alternateURL</code> if it
090: * exists.</li>
091: * <p/>
092: * <li>Each user element has a distinguished name that can be formed by
093: * substituting the presented username into a pattern configured by the
094: * <code>userPattern</code> property.</li>
095: * <p/>
096: * <li>Alternatively, if the <code>userPattern</code> property is not
097: * specified, a unique element can be located by searching the directory
098: * context. In this case:
099: * <ul>
100: * <li>The <code>userSearch</code> pattern specifies the search filter
101: * after substitution of the username.</li>
102: * <li>The <code>userBase</code> property can be set to the element that
103: * is the base of the subtree containing users. If not specified,
104: * the search base is the top-level context.</li>
105: * <li>The <code>userSubtree</code> property can be set to
106: * <code>true</code> if you wish to search the entire subtree of the
107: * directory context. The default value of <code>false</code>
108: * requests a search of only the current level.</li>
109: * </ul>
110: * </li>
111: * <p/>
112: * <li>The user may be authenticated by binding to the directory with the
113: * username and password presented. This method is used when the
114: * <code>userPassword</code> property is not specified.</li>
115: * <p/>
116: * <li>The user may be authenticated by retrieving the value of an attribute
117: * from the directory and comparing it explicitly with the value presented
118: * by the user. This method is used when the <code>userPassword</code>
119: * property is specified, in which case:
120: * <ul>
121: * <li>The element for this user must contain an attribute named by the
122: * <code>userPassword</code> property.
123: * <li>The value of the user password attribute is either a cleartext
124: * String, or the result of passing a cleartext String through the
125: * <code>RealmBase.digest()</code> method (using the standard digest
126: * support included in <code>RealmBase</code>).
127: * <li>The user is considered to be authenticated if the presented
128: * credentials (after being passed through
129: * <code>RealmBase.digest()</code>) are equal to the retrieved value
130: * for the user password attribute.</li>
131: * </ul></li>
132: * <p/>
133: * <li>Each group of users that has been assigned a particular role may be
134: * represented by an individual element in the top level
135: * <code>DirContext</code> that is accessed via the
136: * <code>connectionURL</code> property. This element has the following
137: * characteristics:
138: * <ul>
139: * <li>The set of all possible groups of interest can be selected by a
140: * search pattern configured by the <code>roleSearch</code>
141: * property.</li>
142: * <li>The <code>roleSearch</code> pattern optionally includes pattern
143: * replacements "{0}" for the distinguished name, and/or "{1}" for
144: * the username, of the authenticated user for which roles will be
145: * retrieved.</li>
146: * <li>The <code>roleBase</code> property can be set to the element that
147: * is the base of the search for matching roles. If not specified,
148: * the entire context will be searched.</li>
149: * <li>The <code>roleSubtree</code> property can be set to
150: * <code>true</code> if you wish to search the entire subtree of the
151: * directory context. The default value of <code>false</code>
152: * requests a search of only the current level.</li>
153: * <li>The element includes an attribute (whose name is configured by
154: * the <code>roleName</code> property) containing the name of the
155: * role represented by this element.</li>
156: * </ul></li>
157: * <p/>
158: * <li>In addition, roles may be represented by the values of an attribute
159: * in the user's element whose name is configured by the
160: * <code>userRoleName</code> property.</li>
161: * <p/>
162: * <li>Note that the standard <code><security-role-ref></code> element in
163: * the web application deployment descriptor allows applications to refer
164: * to roles programmatically by names other than those used in the
165: * directory server itself.</li>
166: * </ul>
167: *
168: * @author John Holman
169: * @author Craig R. McClanahan
170: * @version $Revision: 799 $ $Date: 2005-10-25 04:39:39 -0500 (Tue, 25 Oct 2005) $
171: */
172:
173: public class JNDILoginModule implements LoginModule {
174:
175: JNDIAuthenticator authenticator = null;
176: LoginSupport support = null;
177:
178: public static final Logger log = Logger
179: .getLogger(JNDILoginModule.class);
180: /**
181: * Descriptive information about this Realm implementation.
182: */
183: protected String name = null;
184: /**
185: * Should we dereference directory aliases?
186: * See http://java.sun.com/products/jndi/tutorial/ldap/misc/aliases.html
187: */
188: protected String derefAliases = "always";
189:
190: public JNDILoginModule(JNDIAuthenticator authenticator,
191: LoginSupport support) {
192: this .authenticator = authenticator;
193: this .support = support;
194: }
195:
196: public JNDILoginModule() {
197: this .authenticator = new JNDIAuthenticatorImpl();
198: this .support = new LoginSupportImpl();
199: }
200:
201: public void setOptions(Map options) {
202: authenticator.setOptions(options);
203: }
204:
205: public Subject authenticate(String userId, String password)
206: throws AuthenticationException {
207: Subject subject = null;
208: log.debug(ATTEMPTING_TO_AUTHENTICATE + this .getName() + " ("
209: + userId + ")");
210: try {
211: subject = authenticator.authenticate(userId, password);
212: } catch (com.sabre.security.jndi.AuthenticationException e) {
213: throw new AuthenticationException(e.getMessage());
214: }
215: support.populateSubjectPrincipalFromDatabase(subject, userId);
216: log.debug(AUTHENTICATION_SUCCESFULL + this .getName());
217: return subject;
218: }
219:
220: public boolean isCapableOfChangingPasswords() {
221: return false;
222: }
223:
224: public void changePassword(String userId, String password)
225: throws AuthenticationException {
226: throw new UnsupportedOperationException(
227: "change Password not implemented");
228: }
229:
230: public void logout(HttpServletRequest request)
231: throws AuthenticationException {
232: request.getSession().invalidate();
233: }
234:
235: public void setName(String name) {
236: this .name = name;
237: }
238:
239: /**
240: * Return a short name for this Realm implementation.
241: */
242: public String getName() {
243: return this.name;
244: }
245: }
|