KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > security > auth > login > LDAPLoginModule


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.enterprise.security.auth.login;
25
26 import java.util.*;
27 import java.util.logging.Logger JavaDoc;
28 import java.util.logging.Level JavaDoc;
29 import com.sun.logging.LogDomains;
30
31 import javax.security.auth.*;
32 import javax.security.auth.callback.*;
33 import javax.security.auth.login.*;
34 import javax.security.auth.spi.*;
35
36 import javax.naming.*;
37 import javax.naming.directory.*;
38
39 import com.sun.enterprise.security.auth.realm.ldap.LDAPRealm;
40 // the following required for dynamic group support
41
import java.security.acl.Group JavaDoc;
42 import java.security.Principal JavaDoc;
43 import javax.security.auth.x500.X500Principal JavaDoc;
44 // imported from the ldap booster pack
45
import com.sun.jndi.ldap.obj.GroupOfURLs;
46 import javax.security.auth.login.LoginException JavaDoc;
47
48 /**
49  * iAS JAAS LoginModule for an LDAP Realm.
50  *
51  * <P>Refer to the LDAPRealm documentation for necessary and optional
52  * configuration parameters for the iAS LDAP login support.
53  *
54  * <P>There are various ways in which a user can be authenticated using
55  * an LDAP directory. Currently this login module only supports one mode,
56  * 'find and bind'. Other modes may be added as schedules permit.
57  *
58  * <P>Mode: <i>find-bind</i>
59  * <ol>
60  * <li>An LDAP search is issued on the directory starting at base-dn
61  * with the given search-filter (having substituted the user name
62  * in place of %s). If no entries match this search, login fails
63  * and authentication is over.
64  * <li>The DN of the entry which matched the search as the DN
65  * of the user in the directory. If the search-filter
66  * is properly set there should always be a single match; if there are
67  * multiple matches, the first one found is used.
68  * <li>Next an LDAP bind is attempted using the above DN and the provided
69  * password. If this fails, login is considered to have failed and
70  * authentication is over.
71  * <li>Then an LDAP search is issued on the directory starting at
72  * group-base-dn with the given group-search-filter (having
73  * substituted %d for the user DN previously found). From the
74  * matched entry(ies) all the values of group-target are taken
75  * as group names in which the user has membership. If no entries
76  * are found, the group membership is empty.
77  * </ol>
78  *
79  *
80  */

81 public class LDAPLoginModule extends PasswordLoginModule
82 {
83     private String JavaDoc _userDNbase;
84     private String JavaDoc _searchFilter;
85     private String JavaDoc _grpDNbase;
86     private String JavaDoc _grpSearchFilter;
87     private String JavaDoc _grpTarget;
88     private LDAPRealm _ldapRealm;
89     private String JavaDoc[] _dnOnly = {"dn"};
90
91     /**
92      * Performs authentication for the current user.
93      *
94      */

95     protected void authenticate ()
96         throws LoginException JavaDoc
97     {
98         if (!(_currentRealm instanceof LDAPRealm)) {
99             String JavaDoc msg = sm.getString("ldaplm.badrealm");
100             throw new LoginException JavaDoc(msg);
101         }
102         _ldapRealm = (LDAPRealm)_currentRealm;
103         
104                                 // enforce that password cannot be empty.
105
// ldap may grant login on empty password!
106
if (_password == null || _password.length() == 0) {
107             String JavaDoc msg = sm.getString("ldaplm.emptypassword", _username);
108             throw new LoginException JavaDoc(msg);
109         }
110         
111                                 // load configuration attributes
112
_userDNbase = _currentRealm.getProperty(LDAPRealm.PARAM_USERDN);
113         _searchFilter =
114             _currentRealm.getProperty(LDAPRealm.PARAM_SEARCH_FILTER);
115         _grpDNbase = _currentRealm.getProperty(LDAPRealm.PARAM_GRPDN);
116         _grpSearchFilter =
117             _currentRealm.getProperty(LDAPRealm.PARAM_GRP_SEARCH_FILTER);
118         _grpTarget =
119             _currentRealm.getProperty(LDAPRealm.PARAM_GRP_TARGET);
120         
121         String JavaDoc mode = _currentRealm.getProperty(LDAPRealm.PARAM_MODE);
122
123         if (LDAPRealm.MODE_FIND_BIND.equals(mode)) {
124             findAndBind();
125
126         } else {
127             String JavaDoc msg = sm.getString("ldaplm.badmode", mode);
128             throw new LoginException JavaDoc(msg);
129         }
130     }
131
132
133     /**
134      * Supports mode=find-bind. See class documentation.
135      *
136      */

137     private void findAndBind()
138         throws LoginException JavaDoc
139     {
140         // do search for user, substituting %s for username
141
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(_searchFilter);
142         substitute(sb, LDAPRealm.SUBST_SUBJECT_NAME, _username);
143         String JavaDoc userid = sb.toString();
144
145         // attempt to bind as the user
146
DirContext ctx = null;
147         String JavaDoc srcFilter = null;
148         String JavaDoc[] grpList = null;
149         try {
150             ctx = new InitialDirContext(_ldapRealm.getLdapBindProps());
151             String JavaDoc realUserDN = userSearch(ctx,_userDNbase, userid);
152             if (realUserDN == null) {
153                 String JavaDoc msg = sm.getString("ldaplm.usernotfound", _username);
154                 throw new LoginException JavaDoc(msg);
155             }
156
157             boolean bindSuccessful = bindAsUser(realUserDN, _password);
158             if (bindSuccessful == false) {
159                 String JavaDoc msg = sm.getString("ldaplm.bindfailed", realUserDN);
160                 throw new LoginException JavaDoc(msg);
161             }
162
163             // search groups using above connection, substituting %d (and %s)
164
sb = new StringBuffer JavaDoc(_grpSearchFilter);
165             substitute(sb, LDAPRealm.SUBST_SUBJECT_NAME, _username);
166             substitute(sb, LDAPRealm.SUBST_SUBJECT_DN, realUserDN);
167
168             srcFilter = sb.toString();
169             ArrayList groupsList = new ArrayList();
170             groupsList.addAll(groupSearch(ctx, _grpDNbase, srcFilter, _grpTarget));
171             // search filter is constructed internally as
172
// as a groupofURLS
173
groupsList.addAll(dynamicGroupSearch(ctx, _grpDNbase, _grpTarget,
174                 realUserDN));
175             grpList = new String JavaDoc[groupsList.size()];
176             groupsList.toArray(grpList);
177         } catch (Exception JavaDoc e) {
178             throw new LoginException JavaDoc(e.toString() );
179         } finally {
180             if (ctx != null) {
181                 try {
182                     ctx.close();
183                 } catch (NamingException e) {};
184             }
185         }
186
187         if (_logger.isLoggable(Level.FINE)) {
188             _logger.log(Level.FINE, "LDAP:Group search filter: " + srcFilter);
189             StringBuffer JavaDoc gb = new StringBuffer JavaDoc();
190             gb.append("Group memberships found: ");
191             if (grpList != null) {
192                 for (int i=0; i<grpList.length; i++) {
193                     gb.append(" "+grpList[i]);
194                 }
195             } else {
196                 gb.append("(null)");
197             }
198             _logger.log(Level.FINE, "LDAP: "+ gb.toString());
199         }
200         _ldapRealm.setGroupNames(_username, grpList);
201
202         if(_logger.isLoggable(Level.FINE)){
203              _logger.log(Level.FINE, "LDAP: login succeeded for: " + _username);
204         }
205
206         // note that we don't need to copy the grpList here as groupSearch
207
// return a new String array for each call
208
commitAuthentication(_username, _password,
209                              _currentRealm, grpList);
210     }
211
212
213     /**
214      * Do anonymous search for the user. Should be unique if exists.
215      *
216      */

217     private String JavaDoc userSearch(DirContext ctx, String JavaDoc baseDN, String JavaDoc filter)
218     {
219         if (_logger.isLoggable(Level.FINEST)) {
220             _logger.log(Level.FINE, "search: baseDN: "+ baseDN +
221                            " filter: " + filter);
222         }
223             
224         String JavaDoc foundDN = null;
225         NamingEnumeration namingEnum = null;
226
227         SearchControls ctls = new SearchControls();
228         ctls.setReturningAttributes(_dnOnly);
229         ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
230         ctls.setCountLimit(1);
231
232         try {
233             namingEnum = ctx.search(baseDN, filter, ctls);
234             if (namingEnum.hasMore()) {
235                 SearchResult res = (SearchResult)namingEnum.next();
236
237                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
238                 //for dn name with '/'
239
CompositeName compDN = new CompositeName(res.getName());
240                 String JavaDoc ldapDN = compDN.get(0);
241                 sb.append(ldapDN);
242                 
243                 if (res.isRelative()) {
244                     sb.append(",");
245                     sb.append(baseDN);
246                 }
247                 foundDN = sb.toString();
248                 if (_logger.isLoggable(Level.FINEST)) {
249                     _logger.log(Level.FINE, "Found user DN: " + foundDN);
250                 }
251             }
252         } catch (Exception JavaDoc e) {
253             _logger.log(Level.WARNING, "ldaplm.searcherror", filter);
254             _logger.log(Level.WARNING, "security.exception", e);
255         } finally {
256             if (namingEnum != null) {
257                 try {
258                     namingEnum.close();
259                 } catch(Exception JavaDoc ex) {
260                 }
261             }
262         }
263
264         return foundDN;
265     }
266
267
268     /**
269      * Attempt to bind as a specific DN.
270      *
271      */

272     private boolean bindAsUser(String JavaDoc bindDN, String JavaDoc password)
273     {
274         boolean bindSuccessful=false;
275
276         Properties p = _ldapRealm.getLdapBindProps();
277         
278         p.put(Context.SECURITY_PRINCIPAL, bindDN);
279         p.put(Context.SECURITY_CREDENTIALS, password);
280         
281         DirContext ctx = null;
282         try {
283             ctx = new InitialDirContext(p);
284             bindSuccessful = true;
285         } catch (Exception JavaDoc e) {
286             if (_logger.isLoggable(Level.FINEST)) {
287                 _logger.finest("Error binding to directory as: " + bindDN);
288                 _logger.finest("Exception from JNDI: " + e.toString());
289             }
290         } finally {
291             if (ctx != null) {
292                 try {
293                     ctx.close();
294                 } catch (NamingException e) {};
295             }
296         }
297         return bindSuccessful;
298     }
299
300     /**
301      * Search for group membership using the given connection.
302      *
303      */

304     private List dynamicGroupSearch(DirContext ctx, String JavaDoc baseDN,
305             String JavaDoc target, String JavaDoc userDN)
306     {
307         List groupList = new ArrayList();
308         String JavaDoc filter = LDAPRealm.DYNAMIC_GROUP_FILTER;
309         
310         String JavaDoc[] targets = new String JavaDoc[] { target, "memberUrl" };
311
312         try {
313             SearchControls ctls = new SearchControls();
314             ctls.setReturningAttributes(targets);
315             ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
316             ctls.setReturningObjFlag(true);
317             
318             NamingEnumeration e = ctx.search(baseDN, filter, ctls);
319             
320             while(e.hasMore()) {
321                 SearchResult res = (SearchResult)e.next();
322                 Object JavaDoc searchedObject = res.getObject();
323                 if(searchedObject instanceof com.sun.jndi.ldap.obj.GroupOfURLs){ // dynamic group
324
com.sun.jndi.ldap.obj.GroupOfURLs gurls = (com.sun.jndi.ldap.obj.GroupOfURLs) searchedObject;
325                     Principal JavaDoc x500principal = new X500Principal JavaDoc(userDN);
326                     if (gurls.isMember(x500principal)) {
327                         
328                         Attribute grpAttr = res.getAttributes().get(target);
329                         int sz = grpAttr.size();
330                         for (int i=0; i<sz; i++) {
331                             String JavaDoc s = (String JavaDoc)grpAttr.get(i);
332                             groupList.add(s);
333                         }
334                     }
335                 }
336                 // recommended by Jaya Hangal from JDK team
337
if (searchedObject instanceof Context) {
338                     ((Context)searchedObject).close();
339                 }
340             }
341         } catch (Exception JavaDoc e) {
342             _logger.log(Level.WARNING, "ldaplm.searcherror", filter);
343             _logger.log(Level.WARNING, "security.exception", e);
344         }
345         return groupList;
346     }
347     
348     /**
349      * Search for group membership using the given connection.
350      *
351      */

352     private List groupSearch(DirContext ctx, String JavaDoc baseDN,
353                                  String JavaDoc filter, String JavaDoc target)
354     {
355         List groupList = new ArrayList();
356         
357         try {
358             String JavaDoc[] targets = new String JavaDoc[1];
359             targets[0] = target;
360             
361             SearchControls ctls = new SearchControls();
362             ctls.setReturningAttributes(targets);
363             ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
364             
365             NamingEnumeration e = ctx.search(baseDN, filter, ctls);
366             
367             while(e.hasMore()) {
368                 SearchResult res = (SearchResult)e.next();
369                 Attribute grpAttr = res.getAttributes().get(target);
370                 int sz = grpAttr.size();
371                 for (int i=0; i<sz; i++) {
372                     String JavaDoc s = (String JavaDoc)grpAttr.get(i);
373                     groupList.add(s);
374                 }
375             }
376                 
377         } catch (Exception JavaDoc e) {
378             _logger.log(Level.WARNING, "ldaplm.searcherror", filter);
379             _logger.log(Level.WARNING, "security.exception", e);
380         }
381
382         return groupList;
383     }
384
385     /**
386      * Do string substitution. target is replaced by value for all
387      * occurences.
388      *
389      */

390     private static void substitute(StringBuffer JavaDoc sb,
391                                    String JavaDoc target, String JavaDoc value)
392     {
393         int i = sb.indexOf(target);
394         while (i >= 0) {
395             sb.replace(i, i+target.length(), value);
396             i = sb.indexOf(target);
397         }
398     }
399
400 }
401
Popular Tags