KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jguard > ext > authentication > loginmodules > JNDILoginModule


1 /*
2  jGuard is a security framework based on top of jaas (java authentication and authorization security).
3  it is written for web applications, to resolve simply, access control problems.
4  version $Name$
5  http://sourceforge.net/projects/jguard/
6
7  Copyright (C) 2004 Charles GAY
8
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Lesser General Public
11  License as published by the Free Software Foundation; either
12  version 2.1 of the License, or (at your option) any later version.
13
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Lesser General Public License for more details.
18
19  You should have received a copy of the GNU Lesser General Public
20  License along with this library; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23
24  jGuard project home page:
25  http://sourceforge.net/projects/jguard/
26
27  */

28 package net.sf.jguard.ext.authentication.loginmodules;
29
30 import java.text.MessageFormat JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.HashSet JavaDoc;
34 import java.util.Hashtable JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.Set JavaDoc;
39 import java.util.logging.Level JavaDoc;
40 import java.util.logging.Logger JavaDoc;
41
42 import javax.naming.CompositeName JavaDoc;
43 import javax.naming.Context JavaDoc;
44 import javax.naming.InitialContext JavaDoc;
45 import javax.naming.Name JavaDoc;
46 import javax.naming.NameParser JavaDoc;
47 import javax.naming.NamingEnumeration JavaDoc;
48 import javax.naming.NamingException JavaDoc;
49 import javax.naming.directory.Attribute JavaDoc;
50 import javax.naming.directory.Attributes JavaDoc;
51 import javax.naming.directory.DirContext JavaDoc;
52 import javax.naming.directory.SearchControls JavaDoc;
53 import javax.naming.directory.SearchResult JavaDoc;
54 import javax.naming.ldap.Control JavaDoc;
55 import javax.naming.ldap.InitialLdapContext JavaDoc;
56 import javax.security.auth.Subject JavaDoc;
57 import javax.security.auth.callback.CallbackHandler JavaDoc;
58 import javax.security.auth.login.FailedLoginException JavaDoc;
59 import javax.security.auth.login.LoginException JavaDoc;
60 import javax.security.auth.spi.LoginModule JavaDoc;
61
62 import net.sf.jguard.core.authentication.credentials.JGuardCredential;
63 import net.sf.jguard.ext.SecurityConstants;
64 import net.sf.jguard.ext.util.FastBindConnectionControl;
65 import net.sf.jguard.ext.util.JNDIUtils;
66
67 /**
68  * JNDI - related LoginModule.
69  * @author <a HREF="mailto:diabolo512@users.sourceforge.net">Charles Gay</a>
70  * @see LoginModule
71  */

72 public class JNDILoginModule extends UserLoginModule implements LoginModule JavaDoc {
73
74     private static final String JavaDoc USER_DN = "userDN";
75     private static final String JavaDoc CONTEXTFORCOMMIT = "contextforcommit";
76     private static final String JavaDoc JNDI = "jndi";
77     private static final String JavaDoc TIMELIMIT = "timelimit";
78     private static final String JavaDoc SEARCHSCOPE = "searchscope";
79     private static final String JavaDoc RETURNINGOBJFLAG = "returningobjflag";
80     private static final String JavaDoc RETURNINGATTRIBUTES = "returningattributes";
81     private static final String JavaDoc DEREFLINKFLAG = "dereflinkflag";
82     private static final String JavaDoc COUNTLIMIT = "countlimit";
83     private static final String JavaDoc SEARCHCONTROLS = "searchcontrols.";
84     //constants
85
private static final String JavaDoc PREAUTH = "preauth.";
86     private static final String JavaDoc AUTH = "auth.";
87     private static final String JavaDoc FAST_BIND_CONNECTION = "fastBindConnection";
88     private static final String JavaDoc SEARCH_FILTER = "search.filter";
89     private static final String JavaDoc SEARCH_BASE_DN = "search.base.dn";
90     
91     
92     private static final Logger JavaDoc logger = Logger.getLogger(JNDILoginModule.class.getName());
93     
94     
95     private DirContext JavaDoc preAuthContext = null;
96     private DirContext JavaDoc authContext = null;
97     
98     //JNDI SearchControls
99
private SearchControls JavaDoc preAuthSearchControls = null;
100     
101     private Map JavaDoc authOpts = null;
102     private Map JavaDoc preAuthOpts = null;
103     private Map JavaDoc preAuthSearchControlsOpts = null;
104     
105     private Set JavaDoc credentials = null;
106     
107     /**
108      *
109      * @param subj
110      * @param cbkHandler
111      * @param sState
112      * @param opts
113      */

114     public void initialize(Subject JavaDoc subj, CallbackHandler JavaDoc cbkHandler, Map JavaDoc sState, Map JavaDoc opts) {
115         super.initialize(subj, cbkHandler, sState, opts);
116         preAuthOpts = new HashMap JavaDoc();
117         preAuthSearchControlsOpts = new HashMap JavaDoc();
118         authOpts = new HashMap JavaDoc();
119         
120         //populate specialized maps with opts
121
fillOptions();
122         
123     }
124     
125     private DirContext JavaDoc getContext(Map JavaDoc opts) throws LoginException JavaDoc{
126         DirContext JavaDoc context = null;
127         if(opts.containsKey(JNDILoginModule.JNDI)){
128             try {
129                 Context JavaDoc initDirContext = new InitialContext JavaDoc();
130                 context = (DirContext JavaDoc) initDirContext.lookup((String JavaDoc)opts.get(JNDILoginModule.JNDI));
131             } catch (NamingException JavaDoc e) {
132                 throw new LoginException JavaDoc(" we cannot grab the default initial context ");
133             }
134             
135         }else{
136             Control JavaDoc[] LDAPcontrols = getLDAPControls(opts);
137             try {
138                 context = new InitialLdapContext JavaDoc(new Hashtable JavaDoc(opts),LDAPcontrols);
139             } catch (NamingException JavaDoc e) {
140                 throw new LoginException JavaDoc(e.getMessage());
141             }
142         }
143         if(context == null){
144             throw new LoginException JavaDoc(" we cannot grab the default initial context ");
145         }
146         return context;
147         
148     }
149     
150     /**
151      * grab <strong>opts</strong> options and fill preAuthOpts , preAuthSearchControlsOpts and
152      * authOpts options.
153      * @param opts
154      * @param preAuthOpts
155      * @param preAuthSearchControlsOpts
156      * @param authOpts
157      */

158     private void fillOptions() {
159         Iterator JavaDoc entriesIterator = options.entrySet().iterator();
160         while(entriesIterator.hasNext()){
161             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)entriesIterator.next();
162             String JavaDoc key = (String JavaDoc)entry.getKey();
163             String JavaDoc value = (String JavaDoc)entry.getValue();
164             if(key.startsWith(JNDILoginModule.PREAUTH)){
165                 key = key.substring(8, key.length());
166                 if(key.startsWith(JNDILoginModule.SEARCHCONTROLS)){
167                     key = key.substring(15, key.length());
168                 preAuthSearchControlsOpts.put(key, value);
169                 }else{
170                 preAuthOpts.put(key, value);
171                 }
172             }else if(key.startsWith(JNDILoginModule.AUTH)){
173                 key = key.substring(5, key.length());
174                 authOpts.put(key, value);
175             }
176         }
177     }
178
179     /**
180      * @return true if success, false if ignored.
181      * @throws LoginException
182      * when it fails
183      */

184     public boolean login() throws LoginException JavaDoc {
185         super.login();
186         if(SecurityConstants.GUEST.equals(login)){
187             //when user is a guest, we have no need to use this loginmodule
188
loginOK = false;
189             return false;
190         }
191         
192         
193         
194         //userDN is null(not configured) if preAuth is configured
195
//because preAuth is used to find dynamically
196
//the DN of the user
197
String JavaDoc userDN = (String JavaDoc)authOpts.get(USER_DN);
198         if(preAuthOpts.size()==0 &&(userDN==null || userDN.equals(""))){
199             throw new IllegalArgumentException JavaDoc(" you've configured the JNDILoginmodule in 'auth' mode (options starting by 'preauth.' are not present).\n 'auth.userDN' option used to find the user LDAP Entry is lacking or is empty ");
200         }
201         
202         
203         userDN = getuserDN(userDN, login);
204         
205         if (userDN != null && !equals("")) {
206             authOpts.put(Context.SECURITY_PRINCIPAL, userDN);
207             authOpts.put(Context.SECURITY_CREDENTIALS, new String JavaDoc(password));
208             try {
209                 authContext = getContext(authOpts);
210             } finally {
211                 try {
212                     if(authContext!= null){
213                         authContext.close();
214                     }
215                 } catch (NamingException JavaDoc e) {
216                     throw new FailedLoginException JavaDoc(e.getMessage());
217                 }
218             }
219         // authentication succeed
220
}else{
221             loginOK = false;
222             throw new LoginException JavaDoc(" Distinguished name is null or empty ");
223         }
224         //like we've arleady check user credentials against the directory
225
//password check must not be done one more time.
226
sharedState.put(SecurityConstants.SKIP_PASSWORD_CHECK, "true");
227         logger.log(Level.INFO, " JNDI login phase succeed for user "+login);
228         return true;
229     }
230
231     /**
232      * grab the Distinguished Name of the LDAP entry related to the user.
233      * either a pre-authentication can be needed to execute an LDAP search, or
234      * the DN can be calculated from the LDAP filter configured.
235      * @param userDN
236      * @param escapedLogin
237      * @return
238      * @throws LoginException
239      */

240     private String JavaDoc getuserDN(String JavaDoc userDN, String JavaDoc login) throws LoginException JavaDoc {
241         //we prevent LDAP injection from the login
242
String JavaDoc escapedLogin = JNDIUtils.escapeDn(login);
243         Object JavaDoc[] args = {escapedLogin};
244         if(preAuthOpts.size()>0){
245
246             //preauth initialization
247
try {
248                 preAuthContext = getContext(preAuthOpts);
249             } catch (LoginException JavaDoc e) {
250                 loginOK = false;
251                 throw new IllegalArgumentException JavaDoc(e.getMessage());
252             }
253             preAuthSearchControlsOpts.put(COUNTLIMIT, "1");
254             preAuthSearchControls = getSearchControls(preAuthSearchControlsOpts);
255             
256             try{
257             userDN = preAuthSearch(preAuthContext, preAuthSearchControls);
258             }catch(LoginException JavaDoc e){
259                 loginOK = false;
260                 throw e;
261             }finally{
262                 try {
263                     preAuthContext.close();
264                 } catch (NamingException JavaDoc e) {
265                     logger.severe(e.getMessage());
266                 }
267             }
268         }else{
269             userDN = MessageFormat.format(userDN, args);
270             userDN = JNDIUtils.escapeDn(userDN);
271         }
272         return userDN;
273     }
274
275
276
277     /**
278      * @return <strong>true</strong> if success, <strong>false</strong> if ignored,
279      * <strong>LoginException</strong> when it fails.
280      */

281     public boolean commit() throws LoginException JavaDoc {
282         if(!loginOK){
283             return false;
284         }
285         if(options.containsKey(JNDILoginModule.CONTEXTFORCOMMIT)&&options.get(JNDILoginModule.CONTEXTFORCOMMIT).equals("true")){
286             credentials = grabAttributes(getContext(authOpts),(String JavaDoc)authOpts.get(USER_DN));
287         }
288         
289         if(credentials!=null){
290             Set JavaDoc privateCredentials = subject.getPrivateCredentials();
291             privateCredentials.addAll(credentials);
292         }
293         return true;
294     }
295
296     /**
297      * grab the attributes of the specified LDAP entry with userDN
298      * and return a credential Set.
299      * @param contextUsedForCommit
300      * @param userDN
301      * @return
302      * @throws LoginException
303      */

304     private Set JavaDoc grabAttributes(DirContext JavaDoc contextUsedForCommit,String JavaDoc userDN) throws LoginException JavaDoc {
305         DirContext JavaDoc userCtxt = null;
306         Set JavaDoc creds = new HashSet JavaDoc();
307         try {
308             userCtxt = (DirContext JavaDoc) contextUsedForCommit.lookup(getuserDN(userDN,login));
309             if(userCtxt==null){
310                 throw new FailedLoginException JavaDoc("login.user.does.not.exist");
311             }
312
313             Attributes JavaDoc attributes = userCtxt.getAttributes("");
314             creds = grabCredentials(attributes);
315         } catch (NamingException JavaDoc e) {
316             throw new LoginException JavaDoc(e.getMessage());
317         }finally{
318             try {
319                 if(userCtxt!=null){
320                     userCtxt.close();
321                 }
322             } catch (NamingException JavaDoc e) {
323                 throw new LoginException JavaDoc(e.getMessage());
324             }
325         }
326         
327         return creds;
328     }
329     
330     /**
331      * grab attributes of the LDAP entry related to the user and
332      * build a credential Set which contains attributes informations.
333      * @param atts
334      * @return
335      * @throws NamingException
336      */

337     private Set JavaDoc grabCredentials(Attributes JavaDoc atts) throws NamingException JavaDoc {
338         Set JavaDoc credentials = new HashSet JavaDoc();
339         NamingEnumeration JavaDoc enumeration = atts.getAll();
340
341         while(enumeration.hasMore()){
342             Attribute JavaDoc attribute = (Attribute JavaDoc)enumeration.next();
343             String JavaDoc key = attribute.getID();
344             String JavaDoc value= JNDIUtils.getAttributeValue(attribute);
345             JGuardCredential credential = new JGuardCredential();
346             credential.setId(key);
347             credential.setValue(value);
348             credentials.add(credential);
349         }
350         
351         return credentials;
352     }
353     
354     /**
355      * search the Distinguished Name(DN) of the User LDAP entry.
356      * @return Distinguised Name of the User found
357      * @throws LoginException
358      */

359     private String JavaDoc preAuthSearch(DirContext JavaDoc context,SearchControls JavaDoc controls) throws LoginException JavaDoc {
360         NamingEnumeration JavaDoc results = null;
361         String JavaDoc dn = null;
362         String JavaDoc baseDN = null;
363         String JavaDoc searchFilter = null;
364         try {
365                 String JavaDoc[] filterArgs = new String JavaDoc[]{super.login};
366                 Hashtable JavaDoc opts = context.getEnvironment();
367                 baseDN = (String JavaDoc)opts.get(JNDILoginModule.SEARCH_BASE_DN);
368                 searchFilter = (String JavaDoc)opts.get(JNDILoginModule.SEARCH_FILTER);
369                 
370                 results = context.search(baseDN,searchFilter,filterArgs, controls);
371                 int userFound = 0;
372                 boolean grabInformations = false;
373                 String JavaDoc contextforcommit = (String JavaDoc)options.get(JNDILoginModule.CONTEXTFORCOMMIT);
374                 if (contextforcommit!=null && "preauth".equals(contextforcommit)){
375                     grabInformations = true;
376                 }
377                 while(results.hasMore()){
378                     SearchResult JavaDoc result = (SearchResult JavaDoc) results.next();
379                     //the dn grabbed with getName follow the CompositeName syntax
380
dn = result.getName();
381                     //grab the name parser of the LDAP directory
382
NameParser JavaDoc pn = context.getNameParser("");
383                     //clearly declare the String as a CompositeName
384
CompositeName JavaDoc compName = new CompositeName JavaDoc(result.getName());
385                     
386                     //grab the Name instance of the CompoundName (first position in the CompositeName)
387
Name JavaDoc entryName = pn.parse(compName.get(0));
388                     //grab the String representation of the CompoundName
389
//that's a weird way to escape special characters hadnled normally
390
//by Active Directory which has got a special meaning for JNDI
391
// but it works...
392
dn = entryName.toString();
393                     
394                     if(grabInformations){
395                         credentials = grabCredentials(result.getAttributes());
396                     }
397                     
398                     userFound++;
399                 }
400                 if(userFound>1){
401                     logger.warning("more than one Distinguished Name has been found in the Directory for the user="+login);
402                     throw new FailedLoginException JavaDoc("login.error");
403                 }
404         } catch (NamingException JavaDoc e) {
405             throw new LoginException JavaDoc(" a naming exception has been raised when we are looking for the user Distinguished Name "+e.getMessage());
406         }finally{
407             try {
408                 context.close();
409             } catch (NamingException JavaDoc e) {
410                 throw new LoginException JavaDoc(e.getMessage());
411             }
412         }
413         if(dn==null){
414             throw new FailedLoginException JavaDoc("login.error");
415         }
416         return dn;
417     }
418     
419     private SearchControls JavaDoc getSearchControls(Map JavaDoc opts){
420         SearchControls JavaDoc controls = new SearchControls JavaDoc();
421         Iterator JavaDoc itEntries = opts.entrySet().iterator();
422         while(itEntries.hasNext()){
423             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)itEntries.next();
424             String JavaDoc key = (String JavaDoc)entry.getKey();
425             String JavaDoc value = (String JavaDoc)entry.getValue();
426             if(JNDILoginModule.COUNTLIMIT.equals(key)){
427                 long countLimit = Long.parseLong(value);
428                 controls.setCountLimit(countLimit);
429             }else if(JNDILoginModule.DEREFLINKFLAG.equals(key)){
430                 boolean derefLinkFlag = Boolean.valueOf(value).booleanValue();
431                 controls.setDerefLinkFlag(derefLinkFlag);
432             }else if(JNDILoginModule.RETURNINGATTRIBUTES.equals(key)){
433                 String JavaDoc[] returningAttributes = value.split("#");
434                 controls.setReturningAttributes(returningAttributes);
435             }else if(JNDILoginModule.RETURNINGOBJFLAG.equals(key)){
436                 boolean returningobjflag = Boolean.valueOf(value).booleanValue();
437                 controls.setReturningObjFlag(returningobjflag);
438             }else if(JNDILoginModule.SEARCHSCOPE.equals(key)){
439                 int scope = Integer.parseInt(value);
440                 controls.setSearchScope(scope);
441             }else if(JNDILoginModule.TIMELIMIT.equals(key)){
442                 int timelimit = Integer.parseInt(value);
443                 controls.setTimeLimit(timelimit);
444             }
445         }
446         
447         return controls;
448     }
449
450     
451     private Control JavaDoc[] getLDAPControls(Map JavaDoc opts){
452         List JavaDoc ldapControls = new ArrayList JavaDoc();
453         if(opts.containsKey(JNDILoginModule.FAST_BIND_CONNECTION)
454             && "true".equalsIgnoreCase((String JavaDoc)opts.get(JNDILoginModule.FAST_BIND_CONNECTION))){
455             ldapControls.add(new FastBindConnectionControl());
456         }
457         return (Control JavaDoc[]) ldapControls.toArray(new Control JavaDoc[ldapControls.size()]);
458         
459     }
460
461 }
462
Popular Tags