KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > realm > JNDIRealm


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.catalina.realm;
19
20 import java.io.IOException JavaDoc;
21 import java.security.Principal JavaDoc;
22 import java.text.MessageFormat JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Hashtable JavaDoc;
26 import java.util.List JavaDoc;
27
28 import javax.naming.Context JavaDoc;
29 import javax.naming.CommunicationException JavaDoc;
30 import javax.naming.CompositeName JavaDoc;
31 import javax.naming.InvalidNameException JavaDoc;
32 import javax.naming.NameNotFoundException JavaDoc;
33 import javax.naming.NamingEnumeration JavaDoc;
34 import javax.naming.NamingException JavaDoc;
35 import javax.naming.NameParser JavaDoc;
36 import javax.naming.Name JavaDoc;
37 import javax.naming.AuthenticationException JavaDoc;
38 import javax.naming.directory.Attribute JavaDoc;
39 import javax.naming.directory.Attributes JavaDoc;
40 import javax.naming.directory.DirContext JavaDoc;
41 import javax.naming.directory.InitialDirContext JavaDoc;
42 import javax.naming.directory.SearchControls JavaDoc;
43 import javax.naming.directory.SearchResult JavaDoc;
44 import org.apache.catalina.LifecycleException;
45 import org.apache.catalina.util.Base64;
46 import org.apache.tomcat.util.buf.ByteChunk;
47 import org.apache.tomcat.util.buf.CharChunk;
48
49 /**
50  * <p>Implementation of <strong>Realm</strong> that works with a directory
51  * server accessed via the Java Naming and Directory Interface (JNDI) APIs.
52  * The following constraints are imposed on the data structure in the
53  * underlying directory server:</p>
54  * <ul>
55  *
56  * <li>Each user that can be authenticated is represented by an individual
57  * element in the top level <code>DirContext</code> that is accessed
58  * via the <code>connectionURL</code> property.</li>
59  *
60  * <li>If a socket connection can not be made to the <code>connectURL</code>
61  * an attempt will be made to use the <code>alternateURL</code> if it
62  * exists.</li>
63  *
64  * <li>Each user element has a distinguished name that can be formed by
65  * substituting the presented username into a pattern configured by the
66  * <code>userPattern</code> property.</li>
67  *
68  * <li>Alternatively, if the <code>userPattern</code> property is not
69  * specified, a unique element can be located by searching the directory
70  * context. In this case:
71  * <ul>
72  * <li>The <code>userSearch</code> pattern specifies the search filter
73  * after substitution of the username.</li>
74  * <li>The <code>userBase</code> property can be set to the element that
75  * is the base of the subtree containing users. If not specified,
76  * the search base is the top-level context.</li>
77  * <li>The <code>userSubtree</code> property can be set to
78  * <code>true</code> if you wish to search the entire subtree of the
79  * directory context. The default value of <code>false</code>
80  * requests a search of only the current level.</li>
81  * </ul>
82  * </li>
83  *
84  * <li>The user may be authenticated by binding to the directory with the
85  * username and password presented. This method is used when the
86  * <code>userPassword</code> property is not specified.</li>
87  *
88  * <li>The user may be authenticated by retrieving the value of an attribute
89  * from the directory and comparing it explicitly with the value presented
90  * by the user. This method is used when the <code>userPassword</code>
91  * property is specified, in which case:
92  * <ul>
93  * <li>The element for this user must contain an attribute named by the
94  * <code>userPassword</code> property.
95  * <li>The value of the user password attribute is either a cleartext
96  * String, or the result of passing a cleartext String through the
97  * <code>RealmBase.digest()</code> method (using the standard digest
98  * support included in <code>RealmBase</code>).
99  * <li>The user is considered to be authenticated if the presented
100  * credentials (after being passed through
101  * <code>RealmBase.digest()</code>) are equal to the retrieved value
102  * for the user password attribute.</li>
103  * </ul></li>
104  *
105  * <li>Each group of users that has been assigned a particular role may be
106  * represented by an individual element in the top level
107  * <code>DirContext</code> that is accessed via the
108  * <code>connectionURL</code> property. This element has the following
109  * characteristics:
110  * <ul>
111  * <li>The set of all possible groups of interest can be selected by a
112  * search pattern configured by the <code>roleSearch</code>
113  * property.</li>
114  * <li>The <code>roleSearch</code> pattern optionally includes pattern
115  * replacements "{0}" for the distinguished name, and/or "{1}" for
116  * the username, of the authenticated user for which roles will be
117  * retrieved.</li>
118  * <li>The <code>roleBase</code> property can be set to the element that
119  * is the base of the search for matching roles. If not specified,
120  * the entire context will be searched.</li>
121  * <li>The <code>roleSubtree</code> property can be set to
122  * <code>true</code> if you wish to search the entire subtree of the
123  * directory context. The default value of <code>false</code>
124  * requests a search of only the current level.</li>
125  * <li>The element includes an attribute (whose name is configured by
126  * the <code>roleName</code> property) containing the name of the
127  * role represented by this element.</li>
128  * </ul></li>
129  *
130  * <li>In addition, roles may be represented by the values of an attribute
131  * in the user's element whose name is configured by the
132  * <code>userRoleName</code> property.</li>
133  *
134  * <li>Note that the standard <code>&lt;security-role-ref&gt;</code> element in
135  * the web application deployment descriptor allows applications to refer
136  * to roles programmatically by names other than those used in the
137  * directory server itself.</li>
138  * </ul>
139  *
140  * <p><strong>TODO</strong> - Support connection pooling (including message
141  * format objects) so that <code>authenticate()</code> does not have to be
142  * synchronized.</p>
143  *
144  * <p><strong>WARNING</strong> - There is a reported bug against the Netscape
145  * provider code (com.netscape.jndi.ldap.LdapContextFactory) with respect to
146  * successfully authenticated a non-existing user. The
147  * report is here: http://issues.apache.org/bugzilla/show_bug.cgi?id=11210 .
148  * With luck, Netscape has updated their provider code and this is not an
149  * issue. </p>
150  *
151  * @author John Holman
152  * @author Craig R. McClanahan
153  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
154  */

155
156 public class JNDIRealm extends RealmBase {
157
158
159     // ----------------------------------------------------- Instance Variables
160

161     /**
162      * The type of authentication to use
163      */

164     protected String JavaDoc authentication = null;
165
166     /**
167      * The connection username for the server we will contact.
168      */

169     protected String JavaDoc connectionName = null;
170
171
172     /**
173      * The connection password for the server we will contact.
174      */

175     protected String JavaDoc connectionPassword = null;
176
177
178     /**
179      * The connection URL for the server we will contact.
180      */

181     protected String JavaDoc connectionURL = null;
182
183
184     /**
185      * The directory context linking us to our directory server.
186      */

187     protected DirContext JavaDoc context = null;
188
189
190     /**
191      * The JNDI context factory used to acquire our InitialContext. By
192      * default, assumes use of an LDAP server using the standard JNDI LDAP
193      * provider.
194      */

195     protected String JavaDoc contextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
196
197     
198     /**
199      * How aliases should be dereferenced during search operations.
200      */

201     protected String JavaDoc derefAliases = null;
202
203     /**
204      * Constant that holds the name of the environment property for specifying
205      * the manner in which aliases should be dereferenced.
206      */

207     public final static String JavaDoc DEREF_ALIASES = "java.naming.ldap.derefAliases";
208
209     /**
210      * Descriptive information about this Realm implementation.
211      */

212     protected static final String JavaDoc info =
213         "org.apache.catalina.realm.JNDIRealm/1.0";
214
215
216     /**
217      * Descriptive information about this Realm implementation.
218      */

219     protected static final String JavaDoc name = "JNDIRealm";
220
221
222     /**
223      * The protocol that will be used in the communication with the
224      * directory server.
225      */

226     protected String JavaDoc protocol = null;
227
228
229     /**
230      * How should we handle referrals? Microsoft Active Directory can't handle
231      * the default case, so an application authenticating against AD must
232      * set referrals to "follow".
233      */

234     protected String JavaDoc referrals = null;
235
236
237     /**
238      * The base element for user searches.
239      */

240     protected String JavaDoc userBase = "";
241
242
243     /**
244      * The message format used to search for a user, with "{0}" marking
245      * the spot where the username goes.
246      */

247     protected String JavaDoc userSearch = null;
248
249
250     /**
251      * The MessageFormat object associated with the current
252      * <code>userSearch</code>.
253      */

254     protected MessageFormat JavaDoc userSearchFormat = null;
255
256
257     /**
258      * Should we search the entire subtree for matching users?
259      */

260     protected boolean userSubtree = false;
261
262
263     /**
264      * The attribute name used to retrieve the user password.
265      */

266     protected String JavaDoc userPassword = null;
267
268
269     /**
270      * A string of LDAP user patterns or paths, ":"-separated
271      * These will be used to form the distinguished name of a
272      * user, with "{0}" marking the spot where the specified username
273      * goes.
274      * This is similar to userPattern, but allows for multiple searches
275      * for a user.
276      */

277     protected String JavaDoc[] userPatternArray = null;
278
279
280     /**
281      * The message format used to form the distinguished name of a
282      * user, with "{0}" marking the spot where the specified username
283      * goes.
284      */

285     protected String JavaDoc userPattern = null;
286
287
288     /**
289      * An array of MessageFormat objects associated with the current
290      * <code>userPatternArray</code>.
291      */

292     protected MessageFormat JavaDoc[] userPatternFormatArray = null;
293
294
295     /**
296      * The base element for role searches.
297      */

298     protected String JavaDoc roleBase = "";
299
300
301     /**
302      * The MessageFormat object associated with the current
303      * <code>roleSearch</code>.
304      */

305     protected MessageFormat JavaDoc roleFormat = null;
306
307
308     /**
309      * The name of an attribute in the user's entry containing
310      * roles for that user
311      */

312     protected String JavaDoc userRoleName = null;
313
314
315     /**
316      * The name of the attribute containing roles held elsewhere
317      */

318     protected String JavaDoc roleName = null;
319
320
321     /**
322      * The message format used to select roles for a user, with "{0}" marking
323      * the spot where the distinguished name of the user goes.
324      */

325     protected String JavaDoc roleSearch = null;
326
327
328     /**
329      * Should we search the entire subtree for matching memberships?
330      */

331     protected boolean roleSubtree = false;
332
333     /**
334      * An alternate URL, to which, we should connect if connectionURL fails.
335      */

336     protected String JavaDoc alternateURL;
337
338     /**
339      * The number of connection attempts. If greater than zero we use the
340      * alternate url.
341      */

342     protected int connectionAttempt = 0;
343
344     /**
345      * The current user pattern to be used for lookup and binding of a user.
346      */

347     protected int curUserPattern = 0;
348
349     // ------------------------------------------------------------- Properties
350

351     /**
352      * Return the type of authentication to use.
353      */

354     public String JavaDoc getAuthentication() {
355
356         return authentication;
357
358     }
359
360     /**
361      * Set the type of authentication to use.
362      *
363      * @param authentication The authentication
364      */

365     public void setAuthentication(String JavaDoc authentication) {
366
367         this.authentication = authentication;
368
369     }
370
371     /**
372      * Return the connection username for this Realm.
373      */

374     public String JavaDoc getConnectionName() {
375
376         return (this.connectionName);
377
378     }
379
380
381     /**
382      * Set the connection username for this Realm.
383      *
384      * @param connectionName The new connection username
385      */

386     public void setConnectionName(String JavaDoc connectionName) {
387
388         this.connectionName = connectionName;
389
390     }
391
392
393     /**
394      * Return the connection password for this Realm.
395      */

396     public String JavaDoc getConnectionPassword() {
397
398         return (this.connectionPassword);
399
400     }
401
402
403     /**
404      * Set the connection password for this Realm.
405      *
406      * @param connectionPassword The new connection password
407      */

408     public void setConnectionPassword(String JavaDoc connectionPassword) {
409
410         this.connectionPassword = connectionPassword;
411
412     }
413
414
415     /**
416      * Return the connection URL for this Realm.
417      */

418     public String JavaDoc getConnectionURL() {
419
420         return (this.connectionURL);
421
422     }
423
424
425     /**
426      * Set the connection URL for this Realm.
427      *
428      * @param connectionURL The new connection URL
429      */

430     public void setConnectionURL(String JavaDoc connectionURL) {
431
432         this.connectionURL = connectionURL;
433
434     }
435
436
437     /**
438      * Return the JNDI context factory for this Realm.
439      */

440     public String JavaDoc getContextFactory() {
441
442         return (this.contextFactory);
443
444     }
445
446
447     /**
448      * Set the JNDI context factory for this Realm.
449      *
450      * @param contextFactory The new context factory
451      */

452     public void setContextFactory(String JavaDoc contextFactory) {
453
454         this.contextFactory = contextFactory;
455
456     }
457
458     /**
459      * Return the derefAliases setting to be used.
460      */

461     public java.lang.String JavaDoc getDerefAliases() {
462         return derefAliases;
463     }
464     
465     /**
466      * Set the value for derefAliases to be used when searching the directory.
467      *
468      * @param derefAliases New value of property derefAliases.
469      */

470     public void setDerefAliases(java.lang.String JavaDoc derefAliases) {
471       this.derefAliases = derefAliases;
472     }
473
474     /**
475      * Return the protocol to be used.
476      */

477     public String JavaDoc getProtocol() {
478
479         return protocol;
480
481     }
482
483     /**
484      * Set the protocol for this Realm.
485      *
486      * @param protocol The new protocol.
487      */

488     public void setProtocol(String JavaDoc protocol) {
489
490         this.protocol = protocol;
491
492     }
493
494
495     /**
496      * Returns the current settings for handling JNDI referrals.
497      */

498     public String JavaDoc getReferrals () {
499         return referrals;
500     }
501
502
503     /**
504      * How do we handle JNDI referrals? ignore, follow, or throw
505      * (see javax.naming.Context.REFERRAL for more information).
506      */

507     public void setReferrals (String JavaDoc referrals) {
508         this.referrals = referrals;
509     }
510
511
512     /**
513      * Return the base element for user searches.
514      */

515     public String JavaDoc getUserBase() {
516
517         return (this.userBase);
518
519     }
520
521
522     /**
523      * Set the base element for user searches.
524      *
525      * @param userBase The new base element
526      */

527     public void setUserBase(String JavaDoc userBase) {
528
529         this.userBase = userBase;
530
531     }
532
533
534     /**
535      * Return the message format pattern for selecting users in this Realm.
536      */

537     public String JavaDoc getUserSearch() {
538
539         return (this.userSearch);
540
541     }
542
543
544     /**
545      * Set the message format pattern for selecting users in this Realm.
546      *
547      * @param userSearch The new user search pattern
548      */

549     public void setUserSearch(String JavaDoc userSearch) {
550
551         this.userSearch = userSearch;
552         if (userSearch == null)
553             userSearchFormat = null;
554         else
555             userSearchFormat = new MessageFormat JavaDoc(userSearch);
556
557     }
558
559
560     /**
561      * Return the "search subtree for users" flag.
562      */

563     public boolean getUserSubtree() {
564
565         return (this.userSubtree);
566
567     }
568
569
570     /**
571      * Set the "search subtree for users" flag.
572      *
573      * @param userSubtree The new search flag
574      */

575     public void setUserSubtree(boolean userSubtree) {
576
577         this.userSubtree = userSubtree;
578
579     }
580
581
582     /**
583      * Return the user role name attribute name for this Realm.
584      */

585     public String JavaDoc getUserRoleName() {
586
587         return userRoleName;
588     }
589
590
591     /**
592      * Set the user role name attribute name for this Realm.
593      *
594      * @param userRoleName The new userRole name attribute name
595      */

596     public void setUserRoleName(String JavaDoc userRoleName) {
597
598         this.userRoleName = userRoleName;
599
600     }
601
602
603     /**
604      * Return the base element for role searches.
605      */

606     public String JavaDoc getRoleBase() {
607
608         return (this.roleBase);
609
610     }
611
612
613     /**
614      * Set the base element for role searches.
615      *
616      * @param roleBase The new base element
617      */

618     public void setRoleBase(String JavaDoc roleBase) {
619
620         this.roleBase = roleBase;
621
622     }
623
624
625     /**
626      * Return the role name attribute name for this Realm.
627      */

628     public String JavaDoc getRoleName() {
629
630         return (this.roleName);
631
632     }
633
634
635     /**
636      * Set the role name attribute name for this Realm.
637      *
638      * @param roleName The new role name attribute name
639      */

640     public void setRoleName(String JavaDoc roleName) {
641
642         this.roleName = roleName;
643
644     }
645
646
647     /**
648      * Return the message format pattern for selecting roles in this Realm.
649      */

650     public String JavaDoc getRoleSearch() {
651
652         return (this.roleSearch);
653
654     }
655
656
657     /**
658      * Set the message format pattern for selecting roles in this Realm.
659      *
660      * @param roleSearch The new role search pattern
661      */

662     public void setRoleSearch(String JavaDoc roleSearch) {
663
664         this.roleSearch = roleSearch;
665         if (roleSearch == null)
666             roleFormat = null;
667         else
668             roleFormat = new MessageFormat JavaDoc(roleSearch);
669
670     }
671
672
673     /**
674      * Return the "search subtree for roles" flag.
675      */

676     public boolean getRoleSubtree() {
677
678         return (this.roleSubtree);
679
680     }
681
682
683     /**
684      * Set the "search subtree for roles" flag.
685      *
686      * @param roleSubtree The new search flag
687      */

688     public void setRoleSubtree(boolean roleSubtree) {
689
690         this.roleSubtree = roleSubtree;
691
692     }
693
694
695     /**
696      * Return the password attribute used to retrieve the user password.
697      */

698     public String JavaDoc getUserPassword() {
699
700         return (this.userPassword);
701
702     }
703
704
705     /**
706      * Set the password attribute used to retrieve the user password.
707      *
708      * @param userPassword The new password attribute
709      */

710     public void setUserPassword(String JavaDoc userPassword) {
711
712         this.userPassword = userPassword;
713
714     }
715
716
717     /**
718      * Return the message format pattern for selecting users in this Realm.
719      */

720     public String JavaDoc getUserPattern() {
721
722         return (this.userPattern);
723
724     }
725
726
727     /**
728      * Set the message format pattern for selecting users in this Realm.
729      * This may be one simple pattern, or multiple patterns to be tried,
730      * separated by parentheses. (for example, either "cn={0}", or
731      * "(cn={0})(cn={0},o=myorg)" Full LDAP search strings are also supported,
732      * but only the "OR", "|" syntax, so "(|(cn={0})(cn={0},o=myorg))" is
733      * also valid. Complex search strings with &, etc are NOT supported.
734      *
735      * @param userPattern The new user pattern
736      */

737     public void setUserPattern(String JavaDoc userPattern) {
738
739         this.userPattern = userPattern;
740         if (userPattern == null)
741             userPatternArray = null;
742         else {
743             userPatternArray = parseUserPatternString(userPattern);
744             int len = this.userPatternArray.length;
745             userPatternFormatArray = new MessageFormat JavaDoc[len];
746             for (int i=0; i < len; i++) {
747                 userPatternFormatArray[i] =
748                     new MessageFormat JavaDoc(userPatternArray[i]);
749             }
750         }
751     }
752
753
754     /**
755      * Getter for property alternateURL.
756      *
757      * @return Value of property alternateURL.
758      */

759     public String JavaDoc getAlternateURL() {
760
761         return this.alternateURL;
762
763     }
764
765
766     /**
767      * Setter for property alternateURL.
768      *
769      * @param alternateURL New value of property alternateURL.
770      */

771     public void setAlternateURL(String JavaDoc alternateURL) {
772
773         this.alternateURL = alternateURL;
774
775     }
776
777
778     // ---------------------------------------------------------- Realm Methods
779

780
781     /**
782      * Return the Principal associated with the specified username and
783      * credentials, if there is one; otherwise return <code>null</code>.
784      *
785      * If there are any errors with the JDBC connection, executing
786      * the query or anything we return null (don't authenticate). This
787      * event is also logged, and the connection will be closed so that
788      * a subsequent request will automatically re-open it.
789      *
790      * @param username Username of the Principal to look up
791      * @param credentials Password or other credentials to use in
792      * authenticating this username
793      */

794     public Principal JavaDoc authenticate(String JavaDoc username, String JavaDoc credentials) {
795
796         DirContext JavaDoc context = null;
797         Principal JavaDoc principal = null;
798
799         try {
800
801             // Ensure that we have a directory context available
802
context = open();
803
804             // Occassionally the directory context will timeout. Try one more
805
// time before giving up.
806
try {
807
808                 // Authenticate the specified username if possible
809
principal = authenticate(context, username, credentials);
810
811             } catch (CommunicationException JavaDoc e) {
812
813                 // log the exception so we know it's there.
814
containerLog.warn(sm.getString("jndiRealm.exception"), e);
815
816                 // close the connection so we know it will be reopened.
817
if (context != null)
818                     close(context);
819
820                 // open a new directory context.
821
context = open();
822
823                 // Try the authentication again.
824
principal = authenticate(context, username, credentials);
825
826             }
827
828
829             // Release this context
830
release(context);
831
832             // Return the authenticated Principal (if any)
833
return (principal);
834
835         } catch (NamingException JavaDoc e) {
836
837             // Log the problem for posterity
838
containerLog.error(sm.getString("jndiRealm.exception"), e);
839
840             // Close the connection so that it gets reopened next time
841
if (context != null)
842                 close(context);
843
844             // Return "not authenticated" for this request
845
return (null);
846
847         }
848
849     }
850
851
852     // -------------------------------------------------------- Package Methods
853

854
855     // ------------------------------------------------------ Protected Methods
856

857
858     /**
859      * Return the Principal associated with the specified username and
860      * credentials, if there is one; otherwise return <code>null</code>.
861      *
862      * @param context The directory context
863      * @param username Username of the Principal to look up
864      * @param credentials Password or other credentials to use in
865      * authenticating this username
866      *
867      * @exception NamingException if a directory server error occurs
868      */

869     public synchronized Principal JavaDoc authenticate(DirContext JavaDoc context,
870                                                String JavaDoc username,
871                                                String JavaDoc credentials)
872         throws NamingException JavaDoc {
873
874         if (username == null || username.equals("")
875             || credentials == null || credentials.equals(""))
876             return (null);
877
878         if (userPatternArray != null) {
879             for (curUserPattern = 0;
880                  curUserPattern < userPatternFormatArray.length;
881                  curUserPattern++) {
882                 // Retrieve user information
883
User user = getUser(context, username);
884                 if (user != null) {
885                     try {
886                         // Check the user's credentials
887
if (checkCredentials(context, user, credentials)) {
888                             // Search for additional roles
889
List JavaDoc roles = getRoles(context, user);
890                             return (new GenericPrincipal(this,
891                                                          username,
892                                                          credentials,
893                                                          roles));
894                         }
895                     } catch (InvalidNameException JavaDoc ine) {
896                         // Log the problem for posterity
897
containerLog.warn(sm.getString("jndiRealm.exception"), ine);
898                         // ignore; this is probably due to a name not fitting
899
// the search path format exactly, as in a fully-
900
// qualified name being munged into a search path
901
// that already contains cn= or vice-versa
902
}
903                 }
904             }
905             return null;
906         } else {
907             // Retrieve user information
908
User user = getUser(context, username);
909             if (user == null)
910                 return (null);
911
912             // Check the user's credentials
913
if (!checkCredentials(context, user, credentials))
914                 return (null);
915
916             // Search for additional roles
917
List JavaDoc roles = getRoles(context, user);
918
919             // Create and return a suitable Principal for this user
920
return (new GenericPrincipal(this, username, credentials, roles));
921         }
922     }
923
924
925     /**
926      * Return a User object containing information about the user
927      * with the specified username, if found in the directory;
928      * otherwise return <code>null</code>.
929      *
930      * If the <code>userPassword</code> configuration attribute is
931      * specified, the value of that attribute is retrieved from the
932      * user's directory entry. If the <code>userRoleName</code>
933      * configuration attribute is specified, all values of that
934      * attribute are retrieved from the directory entry.
935      *
936      * @param context The directory context
937      * @param username Username to be looked up
938      *
939      * @exception NamingException if a directory server error occurs
940      */

941     protected User getUser(DirContext JavaDoc context, String JavaDoc username)
942         throws NamingException JavaDoc {
943
944         User user = null;
945
946         // Get attributes to retrieve from user entry
947
ArrayList JavaDoc list = new ArrayList JavaDoc();
948         if (userPassword != null)
949             list.add(userPassword);
950         if (userRoleName != null)
951             list.add(userRoleName);
952         String JavaDoc[] attrIds = new String JavaDoc[list.size()];
953         list.toArray(attrIds);
954
955         // Use pattern or search for user entry
956
if (userPatternFormatArray != null) {
957             user = getUserByPattern(context, username, attrIds);
958         } else {
959             user = getUserBySearch(context, username, attrIds);
960         }
961
962         return user;
963     }
964
965
966     /**
967      * Use the <code>UserPattern</code> configuration attribute to
968      * locate the directory entry for the user with the specified
969      * username and return a User object; otherwise return
970      * <code>null</code>.
971      *
972      * @param context The directory context
973      * @param username The username
974      * @param attrIds String[]containing names of attributes to
975      * retrieve.
976      *
977      * @exception NamingException if a directory server error occurs
978      */

979     protected User getUserByPattern(DirContext JavaDoc context,
980                                               String JavaDoc username,
981                                               String JavaDoc[] attrIds)
982         throws NamingException JavaDoc {
983
984         if (username == null || userPatternFormatArray[curUserPattern] == null)
985             return (null);
986
987         // Form the dn from the user pattern
988
String JavaDoc dn = userPatternFormatArray[curUserPattern].format(new String JavaDoc[] { username });
989
990         // Get required attributes from user entry
991
Attributes JavaDoc attrs = null;
992         try {
993             attrs = context.getAttributes(dn, attrIds);
994         } catch (NameNotFoundException JavaDoc e) {
995             return (null);
996         }
997         if (attrs == null)
998             return (null);
999
1000        // Retrieve value of userPassword
1001
String JavaDoc password = null;
1002        if (userPassword != null)
1003            password = getAttributeValue(userPassword, attrs);
1004
1005        // Retrieve values of userRoleName attribute
1006
ArrayList JavaDoc roles = null;
1007        if (userRoleName != null)
1008            roles = addAttributeValues(userRoleName, attrs, roles);
1009
1010        return new User(username, dn, password, roles);
1011    }
1012
1013