Source Code Cross Referenced for JNDIRealm.java in  » Sevlet-Container » apache-tomcat-6.0.14 » org » apache » catalina » realm » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Sevlet Container » apache tomcat 6.0.14 » org.apache.catalina.realm 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         * 
0009:         *      http://www.apache.org/licenses/LICENSE-2.0
0010:         * 
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:
0018:        package org.apache.catalina.realm;
0019:
0020:        import java.io.IOException;
0021:        import java.security.Principal;
0022:        import java.text.MessageFormat;
0023:        import java.util.ArrayList;
0024:        import java.util.Arrays;
0025:        import java.util.Hashtable;
0026:        import java.util.List;
0027:
0028:        import javax.naming.Context;
0029:        import javax.naming.CommunicationException;
0030:        import javax.naming.CompositeName;
0031:        import javax.naming.InvalidNameException;
0032:        import javax.naming.NameNotFoundException;
0033:        import javax.naming.NamingEnumeration;
0034:        import javax.naming.NamingException;
0035:        import javax.naming.NameParser;
0036:        import javax.naming.Name;
0037:        import javax.naming.AuthenticationException;
0038:        import javax.naming.ServiceUnavailableException;
0039:        import javax.naming.directory.Attribute;
0040:        import javax.naming.directory.Attributes;
0041:        import javax.naming.directory.DirContext;
0042:        import javax.naming.directory.InitialDirContext;
0043:        import javax.naming.directory.SearchControls;
0044:        import javax.naming.directory.SearchResult;
0045:        import org.apache.catalina.LifecycleException;
0046:        import org.apache.catalina.util.Base64;
0047:        import org.apache.tomcat.util.buf.ByteChunk;
0048:        import org.apache.tomcat.util.buf.CharChunk;
0049:
0050:        /**
0051:         * <p>Implementation of <strong>Realm</strong> that works with a directory
0052:         * server accessed via the Java Naming and Directory Interface (JNDI) APIs.
0053:         * The following constraints are imposed on the data structure in the
0054:         * underlying directory server:</p>
0055:         * <ul>
0056:         *
0057:         * <li>Each user that can be authenticated is represented by an individual
0058:         *     element in the top level <code>DirContext</code> that is accessed
0059:         *     via the <code>connectionURL</code> property.</li>
0060:         *
0061:         * <li>If a socket connection can not be made to the <code>connectURL</code>
0062:         *     an attempt will be made to use the <code>alternateURL</code> if it
0063:         *     exists.</li>
0064:         *
0065:         * <li>Each user element has a distinguished name that can be formed by
0066:         *     substituting the presented username into a pattern configured by the
0067:         *     <code>userPattern</code> property.</li>
0068:         *
0069:         * <li>Alternatively, if the <code>userPattern</code> property is not
0070:         *     specified, a unique element can be located by searching the directory
0071:         *     context. In this case:
0072:         *     <ul>
0073:         *     <li>The <code>userSearch</code> pattern specifies the search filter
0074:         *         after substitution of the username.</li>
0075:         *     <li>The <code>userBase</code> property can be set to the element that
0076:         *         is the base of the subtree containing users.  If not specified,
0077:         *         the search base is the top-level context.</li>
0078:         *     <li>The <code>userSubtree</code> property can be set to
0079:         *         <code>true</code> if you wish to search the entire subtree of the
0080:         *         directory context.  The default value of <code>false</code>
0081:         *         requests a search of only the current level.</li>
0082:         *    </ul>
0083:         * </li>
0084:         *
0085:         * <li>The user may be authenticated by binding to the directory with the
0086:         *      username and password presented. This method is used when the
0087:         *      <code>userPassword</code> property is not specified.</li>
0088:         *
0089:         * <li>The user may be authenticated by retrieving the value of an attribute
0090:         *     from the directory and comparing it explicitly with the value presented
0091:         *     by the user. This method is used when the <code>userPassword</code>
0092:         *     property is specified, in which case:
0093:         *     <ul>
0094:         *     <li>The element for this user must contain an attribute named by the
0095:         *         <code>userPassword</code> property.
0096:         *     <li>The value of the user password attribute is either a cleartext
0097:         *         String, or the result of passing a cleartext String through the
0098:         *         <code>RealmBase.digest()</code> method (using the standard digest
0099:         *         support included in <code>RealmBase</code>).
0100:         *     <li>The user is considered to be authenticated if the presented
0101:         *         credentials (after being passed through
0102:         *         <code>RealmBase.digest()</code>) are equal to the retrieved value
0103:         *         for the user password attribute.</li>
0104:         *     </ul></li>
0105:         *
0106:         * <li>Each group of users that has been assigned a particular role may be
0107:         *     represented by an individual element in the top level
0108:         *     <code>DirContext</code> that is accessed via the
0109:         *     <code>connectionURL</code> property.  This element has the following
0110:         *     characteristics:
0111:         *     <ul>
0112:         *     <li>The set of all possible groups of interest can be selected by a
0113:         *         search pattern configured by the <code>roleSearch</code>
0114:         *         property.</li>
0115:         *     <li>The <code>roleSearch</code> pattern optionally includes pattern
0116:         *         replacements "{0}" for the distinguished name, and/or "{1}" for
0117:         *         the username, of the authenticated user for which roles will be
0118:         *         retrieved.</li>
0119:         *     <li>The <code>roleBase</code> property can be set to the element that
0120:         *         is the base of the search for matching roles.  If not specified,
0121:         *         the entire context will be searched.</li>
0122:         *     <li>The <code>roleSubtree</code> property can be set to
0123:         *         <code>true</code> if you wish to search the entire subtree of the
0124:         *         directory context.  The default value of <code>false</code>
0125:         *         requests a search of only the current level.</li>
0126:         *     <li>The element includes an attribute (whose name is configured by
0127:         *         the <code>roleName</code> property) containing the name of the
0128:         *         role represented by this element.</li>
0129:         *     </ul></li>
0130:         *
0131:         * <li>In addition, roles may be represented by the values of an attribute
0132:         * in the user's element whose name is configured by the
0133:         * <code>userRoleName</code> property.</li>
0134:         *
0135:         * <li>Note that the standard <code>&lt;security-role-ref&gt;</code> element in
0136:         *     the web application deployment descriptor allows applications to refer
0137:         *     to roles programmatically by names other than those used in the
0138:         *     directory server itself.</li>
0139:         * </ul>
0140:         *
0141:         * <p><strong>TODO</strong> - Support connection pooling (including message
0142:         * format objects) so that <code>authenticate()</code> does not have to be
0143:         * synchronized.</p>
0144:         *
0145:         * <p><strong>WARNING</strong> - There is a reported bug against the Netscape
0146:         * provider code (com.netscape.jndi.ldap.LdapContextFactory) with respect to
0147:         * successfully authenticated a non-existing user. The
0148:         * report is here: http://issues.apache.org/bugzilla/show_bug.cgi?id=11210 .
0149:         * With luck, Netscape has updated their provider code and this is not an
0150:         * issue. </p>
0151:         *
0152:         * @author John Holman
0153:         * @author Craig R. McClanahan
0154:         * @version $Revision: 543693 $ $Date: 2007-06-02 03:42:17 +0200 (sam., 02 juin 2007) $
0155:         */
0156:
0157:        public class JNDIRealm extends RealmBase {
0158:
0159:            // ----------------------------------------------------- Instance Variables
0160:
0161:            /**
0162:             *  The type of authentication to use
0163:             */
0164:            protected String authentication = null;
0165:
0166:            /**
0167:             * The connection username for the server we will contact.
0168:             */
0169:            protected String connectionName = null;
0170:
0171:            /**
0172:             * The connection password for the server we will contact.
0173:             */
0174:            protected String connectionPassword = null;
0175:
0176:            /**
0177:             * The connection URL for the server we will contact.
0178:             */
0179:            protected String connectionURL = null;
0180:
0181:            /**
0182:             * The directory context linking us to our directory server.
0183:             */
0184:            protected DirContext context = null;
0185:
0186:            /**
0187:             * The JNDI context factory used to acquire our InitialContext.  By
0188:             * default, assumes use of an LDAP server using the standard JNDI LDAP
0189:             * provider.
0190:             */
0191:            protected String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
0192:
0193:            /**
0194:             * How aliases should be dereferenced during search operations.
0195:             */
0196:            protected String derefAliases = null;
0197:
0198:            /**
0199:             * Constant that holds the name of the environment property for specifying 
0200:             * the manner in which aliases should be dereferenced.
0201:             */
0202:            public final static String DEREF_ALIASES = "java.naming.ldap.derefAliases";
0203:
0204:            /**
0205:             * Descriptive information about this Realm implementation.
0206:             */
0207:            protected static final String info = "org.apache.catalina.realm.JNDIRealm/1.0";
0208:
0209:            /**
0210:             * Descriptive information about this Realm implementation.
0211:             */
0212:            protected static final String name = "JNDIRealm";
0213:
0214:            /**
0215:             * The protocol that will be used in the communication with the
0216:             * directory server.
0217:             */
0218:            protected String protocol = null;
0219:
0220:            /**
0221:             * How should we handle referrals?  Microsoft Active Directory can't handle
0222:             * the default case, so an application authenticating against AD must
0223:             * set referrals to "follow".
0224:             */
0225:            protected String referrals = null;
0226:
0227:            /**
0228:             * The base element for user searches.
0229:             */
0230:            protected String userBase = "";
0231:
0232:            /**
0233:             * The message format used to search for a user, with "{0}" marking
0234:             * the spot where the username goes.
0235:             */
0236:            protected String userSearch = null;
0237:
0238:            /**
0239:             * The MessageFormat object associated with the current
0240:             * <code>userSearch</code>.
0241:             */
0242:            protected MessageFormat userSearchFormat = null;
0243:
0244:            /**
0245:             * Should we search the entire subtree for matching users?
0246:             */
0247:            protected boolean userSubtree = false;
0248:
0249:            /**
0250:             * The attribute name used to retrieve the user password.
0251:             */
0252:            protected String userPassword = null;
0253:
0254:            /**
0255:             * A string of LDAP user patterns or paths, ":"-separated
0256:             * These will be used to form the distinguished name of a
0257:             * user, with "{0}" marking the spot where the specified username
0258:             * goes.
0259:             * This is similar to userPattern, but allows for multiple searches
0260:             * for a user.
0261:             */
0262:            protected String[] userPatternArray = null;
0263:
0264:            /**
0265:             * The message format used to form the distinguished name of a
0266:             * user, with "{0}" marking the spot where the specified username
0267:             * goes.
0268:             */
0269:            protected String userPattern = null;
0270:
0271:            /**
0272:             * An array of MessageFormat objects associated with the current
0273:             * <code>userPatternArray</code>.
0274:             */
0275:            protected MessageFormat[] userPatternFormatArray = null;
0276:
0277:            /**
0278:             * The base element for role searches.
0279:             */
0280:            protected String roleBase = "";
0281:
0282:            /**
0283:             * The MessageFormat object associated with the current
0284:             * <code>roleSearch</code>.
0285:             */
0286:            protected MessageFormat roleFormat = null;
0287:
0288:            /**
0289:             * The name of an attribute in the user's entry containing
0290:             * roles for that user
0291:             */
0292:            protected String userRoleName = null;
0293:
0294:            /**
0295:             * The name of the attribute containing roles held elsewhere
0296:             */
0297:            protected String roleName = null;
0298:
0299:            /**
0300:             * The message format used to select roles for a user, with "{0}" marking
0301:             * the spot where the distinguished name of the user goes.
0302:             */
0303:            protected String roleSearch = null;
0304:
0305:            /**
0306:             * Should we search the entire subtree for matching memberships?
0307:             */
0308:            protected boolean roleSubtree = false;
0309:
0310:            /**
0311:             * An alternate URL, to which, we should connect if connectionURL fails.
0312:             */
0313:            protected String alternateURL;
0314:
0315:            /**
0316:             * The number of connection attempts.  If greater than zero we use the
0317:             * alternate url.
0318:             */
0319:            protected int connectionAttempt = 0;
0320:
0321:            /**
0322:             * The current user pattern to be used for lookup and binding of a user.
0323:             */
0324:            protected int curUserPattern = 0;
0325:
0326:            // ------------------------------------------------------------- Properties
0327:
0328:            /**
0329:             * Return the type of authentication to use.
0330:             */
0331:            public String getAuthentication() {
0332:
0333:                return authentication;
0334:
0335:            }
0336:
0337:            /**
0338:             * Set the type of authentication to use.
0339:             *
0340:             * @param authentication The authentication
0341:             */
0342:            public void setAuthentication(String authentication) {
0343:
0344:                this .authentication = authentication;
0345:
0346:            }
0347:
0348:            /**
0349:             * Return the connection username for this Realm.
0350:             */
0351:            public String getConnectionName() {
0352:
0353:                return (this .connectionName);
0354:
0355:            }
0356:
0357:            /**
0358:             * Set the connection username for this Realm.
0359:             *
0360:             * @param connectionName The new connection username
0361:             */
0362:            public void setConnectionName(String connectionName) {
0363:
0364:                this .connectionName = connectionName;
0365:
0366:            }
0367:
0368:            /**
0369:             * Return the connection password for this Realm.
0370:             */
0371:            public String getConnectionPassword() {
0372:
0373:                return (this .connectionPassword);
0374:
0375:            }
0376:
0377:            /**
0378:             * Set the connection password for this Realm.
0379:             *
0380:             * @param connectionPassword The new connection password
0381:             */
0382:            public void setConnectionPassword(String connectionPassword) {
0383:
0384:                this .connectionPassword = connectionPassword;
0385:
0386:            }
0387:
0388:            /**
0389:             * Return the connection URL for this Realm.
0390:             */
0391:            public String getConnectionURL() {
0392:
0393:                return (this .connectionURL);
0394:
0395:            }
0396:
0397:            /**
0398:             * Set the connection URL for this Realm.
0399:             *
0400:             * @param connectionURL The new connection URL
0401:             */
0402:            public void setConnectionURL(String connectionURL) {
0403:
0404:                this .connectionURL = connectionURL;
0405:
0406:            }
0407:
0408:            /**
0409:             * Return the JNDI context factory for this Realm.
0410:             */
0411:            public String getContextFactory() {
0412:
0413:                return (this .contextFactory);
0414:
0415:            }
0416:
0417:            /**
0418:             * Set the JNDI context factory for this Realm.
0419:             *
0420:             * @param contextFactory The new context factory
0421:             */
0422:            public void setContextFactory(String contextFactory) {
0423:
0424:                this .contextFactory = contextFactory;
0425:
0426:            }
0427:
0428:            /**
0429:             * Return the derefAliases setting to be used.
0430:             */
0431:            public java.lang.String getDerefAliases() {
0432:                return derefAliases;
0433:            }
0434:
0435:            /**
0436:             * Set the value for derefAliases to be used when searching the directory.
0437:             * 
0438:             * @param derefAliases New value of property derefAliases.
0439:             */
0440:            public void setDerefAliases(java.lang.String derefAliases) {
0441:                this .derefAliases = derefAliases;
0442:            }
0443:
0444:            /**
0445:             * Return the protocol to be used.
0446:             */
0447:            public String getProtocol() {
0448:
0449:                return protocol;
0450:
0451:            }
0452:
0453:            /**
0454:             * Set the protocol for this Realm.
0455:             *
0456:             * @param protocol The new protocol.
0457:             */
0458:            public void setProtocol(String protocol) {
0459:
0460:                this .protocol = protocol;
0461:
0462:            }
0463:
0464:            /**
0465:             * Returns the current settings for handling JNDI referrals.
0466:             */
0467:            public String getReferrals() {
0468:                return referrals;
0469:            }
0470:
0471:            /**
0472:             * How do we handle JNDI referrals? ignore, follow, or throw
0473:             * (see javax.naming.Context.REFERRAL for more information).
0474:             */
0475:            public void setReferrals(String referrals) {
0476:                this .referrals = referrals;
0477:            }
0478:
0479:            /**
0480:             * Return the base element for user searches.
0481:             */
0482:            public String getUserBase() {
0483:
0484:                return (this .userBase);
0485:
0486:            }
0487:
0488:            /**
0489:             * Set the base element for user searches.
0490:             *
0491:             * @param userBase The new base element
0492:             */
0493:            public void setUserBase(String userBase) {
0494:
0495:                this .userBase = userBase;
0496:
0497:            }
0498:
0499:            /**
0500:             * Return the message format pattern for selecting users in this Realm.
0501:             */
0502:            public String getUserSearch() {
0503:
0504:                return (this .userSearch);
0505:
0506:            }
0507:
0508:            /**
0509:             * Set the message format pattern for selecting users in this Realm.
0510:             *
0511:             * @param userSearch The new user search pattern
0512:             */
0513:            public void setUserSearch(String userSearch) {
0514:
0515:                this .userSearch = userSearch;
0516:                if (userSearch == null)
0517:                    userSearchFormat = null;
0518:                else
0519:                    userSearchFormat = new MessageFormat(userSearch);
0520:
0521:            }
0522:
0523:            /**
0524:             * Return the "search subtree for users" flag.
0525:             */
0526:            public boolean getUserSubtree() {
0527:
0528:                return (this .userSubtree);
0529:
0530:            }
0531:
0532:            /**
0533:             * Set the "search subtree for users" flag.
0534:             *
0535:             * @param userSubtree The new search flag
0536:             */
0537:            public void setUserSubtree(boolean userSubtree) {
0538:
0539:                this .userSubtree = userSubtree;
0540:
0541:            }
0542:
0543:            /**
0544:             * Return the user role name attribute name for this Realm.
0545:             */
0546:            public String getUserRoleName() {
0547:
0548:                return userRoleName;
0549:            }
0550:
0551:            /**
0552:             * Set the user role name attribute name for this Realm.
0553:             *
0554:             * @param userRoleName The new userRole name attribute name
0555:             */
0556:            public void setUserRoleName(String userRoleName) {
0557:
0558:                this .userRoleName = userRoleName;
0559:
0560:            }
0561:
0562:            /**
0563:             * Return the base element for role searches.
0564:             */
0565:            public String getRoleBase() {
0566:
0567:                return (this .roleBase);
0568:
0569:            }
0570:
0571:            /**
0572:             * Set the base element for role searches.
0573:             *
0574:             * @param roleBase The new base element
0575:             */
0576:            public void setRoleBase(String roleBase) {
0577:
0578:                this .roleBase = roleBase;
0579:
0580:            }
0581:
0582:            /**
0583:             * Return the role name attribute name for this Realm.
0584:             */
0585:            public String getRoleName() {
0586:
0587:                return (this .roleName);
0588:
0589:            }
0590:
0591:            /**
0592:             * Set the role name attribute name for this Realm.
0593:             *
0594:             * @param roleName The new role name attribute name
0595:             */
0596:            public void setRoleName(String roleName) {
0597:
0598:                this .roleName = roleName;
0599:
0600:            }
0601:
0602:            /**
0603:             * Return the message format pattern for selecting roles in this Realm.
0604:             */
0605:            public String getRoleSearch() {
0606:
0607:                return (this .roleSearch);
0608:
0609:            }
0610:
0611:            /**
0612:             * Set the message format pattern for selecting roles in this Realm.
0613:             *
0614:             * @param roleSearch The new role search pattern
0615:             */
0616:            public void setRoleSearch(String roleSearch) {
0617:
0618:                this .roleSearch = roleSearch;
0619:                if (roleSearch == null)
0620:                    roleFormat = null;
0621:                else
0622:                    roleFormat = new MessageFormat(roleSearch);
0623:
0624:            }
0625:
0626:            /**
0627:             * Return the "search subtree for roles" flag.
0628:             */
0629:            public boolean getRoleSubtree() {
0630:
0631:                return (this .roleSubtree);
0632:
0633:            }
0634:
0635:            /**
0636:             * Set the "search subtree for roles" flag.
0637:             *
0638:             * @param roleSubtree The new search flag
0639:             */
0640:            public void setRoleSubtree(boolean roleSubtree) {
0641:
0642:                this .roleSubtree = roleSubtree;
0643:
0644:            }
0645:
0646:            /**
0647:             * Return the password attribute used to retrieve the user password.
0648:             */
0649:            public String getUserPassword() {
0650:
0651:                return (this .userPassword);
0652:
0653:            }
0654:
0655:            /**
0656:             * Set the password attribute used to retrieve the user password.
0657:             *
0658:             * @param userPassword The new password attribute
0659:             */
0660:            public void setUserPassword(String userPassword) {
0661:
0662:                this .userPassword = userPassword;
0663:
0664:            }
0665:
0666:            /**
0667:             * Return the message format pattern for selecting users in this Realm.
0668:             */
0669:            public String getUserPattern() {
0670:
0671:                return (this .userPattern);
0672:
0673:            }
0674:
0675:            /**
0676:             * Set the message format pattern for selecting users in this Realm.
0677:             * This may be one simple pattern, or multiple patterns to be tried,
0678:             * separated by parentheses. (for example, either "cn={0}", or
0679:             * "(cn={0})(cn={0},o=myorg)" Full LDAP search strings are also supported,
0680:             * but only the "OR", "|" syntax, so "(|(cn={0})(cn={0},o=myorg))" is
0681:             * also valid. Complex search strings with &, etc are NOT supported.
0682:             *
0683:             * @param userPattern The new user pattern
0684:             */
0685:            public void setUserPattern(String userPattern) {
0686:
0687:                this .userPattern = userPattern;
0688:                if (userPattern == null)
0689:                    userPatternArray = null;
0690:                else {
0691:                    userPatternArray = parseUserPatternString(userPattern);
0692:                    int len = this .userPatternArray.length;
0693:                    userPatternFormatArray = new MessageFormat[len];
0694:                    for (int i = 0; i < len; i++) {
0695:                        userPatternFormatArray[i] = new MessageFormat(
0696:                                userPatternArray[i]);
0697:                    }
0698:                }
0699:            }
0700:
0701:            /**
0702:             * Getter for property alternateURL.
0703:             *
0704:             * @return Value of property alternateURL.
0705:             */
0706:            public String getAlternateURL() {
0707:
0708:                return this .alternateURL;
0709:
0710:            }
0711:
0712:            /**
0713:             * Setter for property alternateURL.
0714:             *
0715:             * @param alternateURL New value of property alternateURL.
0716:             */
0717:            public void setAlternateURL(String alternateURL) {
0718:
0719:                this .alternateURL = alternateURL;
0720:
0721:            }
0722:
0723:            // ---------------------------------------------------------- Realm Methods
0724:
0725:            /**
0726:             * Return the Principal associated with the specified username and
0727:             * credentials, if there is one; otherwise return <code>null</code>.
0728:             *
0729:             * If there are any errors with the JDBC connection, executing
0730:             * the query or anything we return null (don't authenticate). This
0731:             * event is also logged, and the connection will be closed so that
0732:             * a subsequent request will automatically re-open it.
0733:             *
0734:             * @param username Username of the Principal to look up
0735:             * @param credentials Password or other credentials to use in
0736:             *  authenticating this username
0737:             */
0738:            public Principal authenticate(String username, String credentials) {
0739:
0740:                DirContext context = null;
0741:                Principal principal = null;
0742:
0743:                try {
0744:
0745:                    // Ensure that we have a directory context available
0746:                    context = open();
0747:
0748:                    // Occassionally the directory context will timeout.  Try one more
0749:                    // time before giving up.
0750:                    try {
0751:
0752:                        // Authenticate the specified username if possible
0753:                        principal = authenticate(context, username, credentials);
0754:
0755:                    } catch (NullPointerException e) {
0756:                        /* BZ 42449 - Kludge Sun's LDAP provider
0757:                           with broken SSL
0758:                         */
0759:                        // log the exception so we know it's there.
0760:                        containerLog.warn(sm.getString("jndiRealm.exception"),
0761:                                e);
0762:
0763:                        // close the connection so we know it will be reopened.
0764:                        if (context != null)
0765:                            close(context);
0766:
0767:                        // open a new directory context.
0768:                        context = open();
0769:
0770:                        // Try the authentication again.
0771:                        principal = authenticate(context, username, credentials);
0772:
0773:                    } catch (CommunicationException e) {
0774:
0775:                        // log the exception so we know it's there.
0776:                        containerLog.warn(sm.getString("jndiRealm.exception"),
0777:                                e);
0778:
0779:                        // close the connection so we know it will be reopened.
0780:                        if (context != null)
0781:                            close(context);
0782:
0783:                        // open a new directory context.
0784:                        context = open();
0785:
0786:                        // Try the authentication again.
0787:                        principal = authenticate(context, username, credentials);
0788:
0789:                    } catch (ServiceUnavailableException e) {
0790:
0791:                        // log the exception so we know it's there.
0792:                        containerLog.warn(sm.getString("jndiRealm.exception"),
0793:                                e);
0794:
0795:                        // close the connection so we know it will be reopened.
0796:                        if (context != null)
0797:                            close(context);
0798:
0799:                        // open a new directory context.
0800:                        context = open();
0801:
0802:                        // Try the authentication again.
0803:                        principal = authenticate(context, username, credentials);
0804:
0805:                    }
0806:
0807:                    // Release this context
0808:                    release(context);
0809:
0810:                    // Return the authenticated Principal (if any)
0811:                    return (principal);
0812:
0813:                } catch (NamingException e) {
0814:
0815:                    // Log the problem for posterity
0816:                    containerLog.error(sm.getString("jndiRealm.exception"), e);
0817:
0818:                    // Close the connection so that it gets reopened next time
0819:                    if (context != null)
0820:                        close(context);
0821:
0822:                    // Return "not authenticated" for this request
0823:                    return (null);
0824:
0825:                }
0826:
0827:            }
0828:
0829:            // -------------------------------------------------------- Package Methods
0830:
0831:            // ------------------------------------------------------ Protected Methods
0832:
0833:            /**
0834:             * Return the Principal associated with the specified username and
0835:             * credentials, if there is one; otherwise return <code>null</code>.
0836:             *
0837:             * @param context The directory context
0838:             * @param username Username of the Principal to look up
0839:             * @param credentials Password or other credentials to use in
0840:             *  authenticating this username
0841:             *
0842:             * @exception NamingException if a directory server error occurs
0843:             */
0844:            public synchronized Principal authenticate(DirContext context,
0845:                    String username, String credentials) throws NamingException {
0846:
0847:                if (username == null || username.equals("")
0848:                        || credentials == null || credentials.equals(""))
0849:                    return (null);
0850:
0851:                if (userPatternArray != null) {
0852:                    for (curUserPattern = 0; curUserPattern < userPatternFormatArray.length; curUserPattern++) {
0853:                        // Retrieve user information
0854:                        User user = getUser(context, username);
0855:                        if (user != null) {
0856:                            try {
0857:                                // Check the user's credentials
0858:                                if (checkCredentials(context, user, credentials)) {
0859:                                    // Search for additional roles
0860:                                    List<String> roles = getRoles(context, user);
0861:                                    return (new GenericPrincipal(this ,
0862:                                            username, credentials, roles));
0863:                                }
0864:                            } catch (InvalidNameException ine) {
0865:                                // Log the problem for posterity
0866:                                containerLog.warn(sm
0867:                                        .getString("jndiRealm.exception"), ine);
0868:                                // ignore; this is probably due to a name not fitting
0869:                                // the search path format exactly, as in a fully-
0870:                                // qualified name being munged into a search path
0871:                                // that already contains cn= or vice-versa
0872:                            }
0873:                        }
0874:                    }
0875:                    return null;
0876:                } else {
0877:                    // Retrieve user information
0878:                    User user = getUser(context, username);
0879:                    if (user == null)
0880:                        return (null);
0881:
0882:                    // Check the user's credentials
0883:                    if (!checkCredentials(context, user, credentials))
0884:                        return (null);
0885:
0886:                    // Search for additional roles
0887:                    List<String> roles = getRoles(context, user);
0888:
0889:                    // Create and return a suitable Principal for this user
0890:                    return (new GenericPrincipal(this , username, credentials,
0891:                            roles));
0892:                }
0893:            }
0894:
0895:            /**
0896:             * Return a User object containing information about the user
0897:             * with the specified username, if found in the directory;
0898:             * otherwise return <code>null</code>.
0899:             *
0900:             * If the <code>userPassword</code> configuration attribute is
0901:             * specified, the value of that attribute is retrieved from the
0902:             * user's directory entry. If the <code>userRoleName</code>
0903:             * configuration attribute is specified, all values of that
0904:             * attribute are retrieved from the directory entry.
0905:             *
0906:             * @param context The directory context
0907:             * @param username Username to be looked up
0908:             *
0909:             * @exception NamingException if a directory server error occurs
0910:             */
0911:            protected User getUser(DirContext context, String username)
0912:                    throws NamingException {
0913:
0914:                User user = null;
0915:
0916:                // Get attributes to retrieve from user entry
0917:                ArrayList<String> list = new ArrayList<String>();
0918:                if (userPassword != null)
0919:                    list.add(userPassword);
0920:                if (userRoleName != null)
0921:                    list.add(userRoleName);
0922:                String[] attrIds = new String[list.size()];
0923:                list.toArray(attrIds);
0924:
0925:                // Use pattern or search for user entry
0926:                if (userPatternFormatArray != null) {
0927:                    user = getUserByPattern(context, username, attrIds);
0928:                } else {
0929:                    user = getUserBySearch(context, username, attrIds);
0930:                }
0931:
0932:                return user;
0933:            }
0934:
0935:            /**
0936:             * Use the <code>UserPattern</code> configuration attribute to
0937:             * locate the directory entry for the user with the specified
0938:             * username and return a User object; otherwise return
0939:             * <code>null</code>.
0940:             *
0941:             * @param context The directory context
0942:             * @param username The username
0943:             * @param attrIds String[]containing names of attributes to
0944:             * retrieve.
0945:             *
0946:             * @exception NamingException if a directory server error occurs
0947:             */
0948:            protected User getUserByPattern(DirContext context,
0949:                    String username, String[] attrIds) throws NamingException {
0950:
0951:                if (username == null
0952:                        || userPatternFormatArray[curUserPattern] == null)
0953:                    return (null);
0954:
0955:                // Form the dn from the user pattern
0956:                String dn = userPatternFormatArray[curUserPattern]
0957:                        .format(new String[] { username });
0958:
0959:                // Get required attributes from user entry
0960:                Attributes attrs = null;
0961:                try {
0962:                    attrs = context.getAttributes(dn, attrIds);
0963:                } catch (NameNotFoundException e) {
0964:                    return (null);
0965:                }
0966:                if (attrs == null)
0967:                    return (null);
0968:
0969:                // Retrieve value of userPassword
0970:                String password = null;
0971:                if (userPassword != null)
0972:                    password = getAttributeValue(userPassword, attrs);
0973:
0974:                // Retrieve values of userRoleName attribute
0975:                ArrayList<String> roles = null;
0976:                if (userRoleName != null)
0977:                    roles = addAttributeValues(userRoleName, attrs, roles);
0978:
0979:                return new User(username, dn, password, roles);
0980:            }
0981:
0982:            /**
0983:             * Search the directory to return a User object containing
0984:             * information about the user with the specified username, if
0985:             * found in the directory; otherwise return <code>null</code>.
0986:             *
0987:             * @param context The directory context
0988:             * @param username The username
0989:             * @param attrIds String[]containing names of attributes to retrieve.
0990:             *
0991:             * @exception NamingException if a directory server error occurs
0992:             */
0993:            protected User getUserBySearch(DirContext context, String username,
0994:                    String[] attrIds) throws NamingException {
0995:
0996:                if (username == null || userSearchFormat == null)
0997:                    return (null);
0998:
0999:                // Form the search filter
1000:                String filter = userSearchFormat
1001:                        .format(new String[] { username });
1002:
1003:                // Set up the search controls
1004:                SearchControls constraints = new SearchControls();
1005:
1006:                if (userSubtree) {
1007:                    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
1008:                } else {
1009:                    constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
1010:                }
1011:
1012:                // Specify the attributes to be retrieved
1013:                if (attrIds == null)
1014:                    attrIds = new String[0];
1015:                constraints.setReturningAttributes(attrIds);
1016:
1017:                NamingEnumeration results = context.search(userBase, filter,
1018:                        constraints);
1019:
1020:                // Fail if no entries found
1021:                if (results == null || !results.hasMore()) {
1022:                    return (null);
1023:                }
1024:
1025:                // Get result for the first entry found
1026:                SearchResult result = (SearchResult) results.next();
1027:
1028:                // Check no further entries were found
1029:                if (results.hasMore()) {
1030:                    if (containerLog.isInfoEnabled())
1031:                        containerLog.info("username " + username
1032:                                + " has multiple entries");
1033:                    return (null);
1034:                }
1035:
1036:                // Get the entry's distinguished name
1037:                NameParser parser = context.getNameParser("");
1038:                Name contextName = parser.parse(context.getNameInNamespace());
1039:                Name baseName = parser.parse(userBase);
1040:
1041:                // Bugzilla 32269
1042:                Name entryName = parser.parse(new CompositeName(result
1043:                        .getName()).get(0));
1044:
1045:                Name name = contextName.addAll(baseName);
1046:                name = name.addAll(entryName);
1047:                String dn = name.toString();
1048:
1049:                if (containerLog.isTraceEnabled())
1050:                    containerLog.trace("  entry found for " + username
1051:                            + " with dn " + dn);
1052:
1053:                // Get the entry's attributes
1054:                Attributes attrs = result.getAttributes();
1055:                if (attrs == null)
1056:                    return null;
1057:
1058:                // Retrieve value of userPassword
1059:                String password = null;
1060:                if (userPassword != null)
1061:                    password = getAttributeValue(userPassword, attrs);
1062:
1063:                // Retrieve values of userRoleName attribute
1064:                ArrayList<String> roles = null;
1065:                if (userRoleName != null)
1066:                    roles = addAttributeValues(userRoleName, attrs, roles);
1067:
1068:                return new User(username, dn, password, roles);
1069:            }
1070:
1071:            /**
1072:             * Check whether the given User can be authenticated with the
1073:             * given credentials. If the <code>userPassword</code>
1074:             * configuration attribute is specified, the credentials
1075:             * previously retrieved from the directory are compared explicitly
1076:             * with those presented by the user. Otherwise the presented
1077:             * credentials are checked by binding to the directory as the
1078:             * user.
1079:             *
1080:             * @param context The directory context
1081:             * @param user The User to be authenticated
1082:             * @param credentials The credentials presented by the user
1083:             *
1084:             * @exception NamingException if a directory server error occurs
1085:             */
1086:            protected boolean checkCredentials(DirContext context, User user,
1087:                    String credentials) throws NamingException {
1088:
1089:                boolean validated = false;
1090:
1091:                if (userPassword == null) {
1092:                    validated = bindAsUser(context, user, credentials);
1093:                } else {
1094:                    validated = compareCredentials(context, user, credentials);
1095:                }
1096:
1097:                if (containerLog.isTraceEnabled()) {
1098:                    if (validated) {
1099:                        containerLog
1100:                                .trace(sm.getString(
1101:                                        "jndiRealm.authenticateSuccess",
1102:                                        user.username));
1103:                    } else {
1104:                        containerLog
1105:                                .trace(sm.getString(
1106:                                        "jndiRealm.authenticateFailure",
1107:                                        user.username));
1108:                    }
1109:                }
1110:                return (validated);
1111:            }
1112:
1113:            /**
1114:             * Check whether the credentials presented by the user match those
1115:             * retrieved from the directory.
1116:             *
1117:             * @param context The directory context
1118:             * @param info The User to be authenticated
1119:             * @param credentials Authentication credentials
1120:             *
1121:             * @exception NamingException if a directory server error occurs
1122:             */
1123:            protected boolean compareCredentials(DirContext context, User info,
1124:                    String credentials) throws NamingException {
1125:
1126:                if (info == null || credentials == null)
1127:                    return (false);
1128:
1129:                String password = info.password;
1130:                if (password == null)
1131:                    return (false);
1132:
1133:                // Validate the credentials specified by the user
1134:                if (containerLog.isTraceEnabled())
1135:                    containerLog.trace("  validating credentials");
1136:
1137:                boolean validated = false;
1138:                if (hasMessageDigest()) {
1139:                    // iPlanet support if the values starts with {SHA1}
1140:                    // The string is in a format compatible with Base64.encode not
1141:                    // the Hex encoding of the parent class.
1142:                    if (password.startsWith("{SHA}")) {
1143:                        /* sync since super.digest() does this same thing */
1144:                        synchronized (this ) {
1145:                            password = password.substring(5);
1146:                            md.reset();
1147:                            md.update(credentials.getBytes());
1148:                            String digestedPassword = new String(Base64
1149:                                    .encode(md.digest()));
1150:                            validated = password.equals(digestedPassword);
1151:                        }
1152:                    } else if (password.startsWith("{SSHA}")) {
1153:                        // Bugzilla 32938
1154:                        /* sync since super.digest() does this same thing */
1155:                        synchronized (this ) {
1156:                            password = password.substring(6);
1157:
1158:                            md.reset();
1159:                            md.update(credentials.getBytes());
1160:
1161:                            // Decode stored password.
1162:                            ByteChunk pwbc = new ByteChunk(password.length());
1163:                            try {
1164:                                pwbc.append(password.getBytes(), 0, password
1165:                                        .length());
1166:                            } catch (IOException e) {
1167:                                // Should never happen
1168:                                containerLog
1169:                                        .error(
1170:                                                "Could not append password bytes to chunk: ",
1171:                                                e);
1172:                            }
1173:
1174:                            CharChunk decoded = new CharChunk();
1175:                            Base64.decode(pwbc, decoded);
1176:                            char[] pwarray = decoded.getBuffer();
1177:
1178:                            // Split decoded password into hash and salt.
1179:                            final int saltpos = 20;
1180:                            byte[] hash = new byte[saltpos];
1181:                            for (int i = 0; i < hash.length; i++) {
1182:                                hash[i] = (byte) pwarray[i];
1183:                            }
1184:
1185:                            byte[] salt = new byte[pwarray.length - saltpos];
1186:                            for (int i = 0; i < salt.length; i++)
1187:                                salt[i] = (byte) pwarray[i + saltpos];
1188:
1189:                            md.update(salt);
1190:                            byte[] dp = md.digest();
1191:
1192:                            validated = Arrays.equals(dp, hash);
1193:                        } // End synchronized(this) block
1194:                    } else {
1195:                        // Hex hashes should be compared case-insensitive
1196:                        validated = (digest(credentials)
1197:                                .equalsIgnoreCase(password));
1198:                    }
1199:                } else
1200:                    validated = (digest(credentials).equals(password));
1201:                return (validated);
1202:
1203:            }
1204:
1205:            /**
1206:             * Check credentials by binding to the directory as the user
1207:             *
1208:             * @param context The directory context
1209:             * @param user The User to be authenticated
1210:             * @param credentials Authentication credentials
1211:             *
1212:             * @exception NamingException if a directory server error occurs
1213:             */
1214:            protected boolean bindAsUser(DirContext context, User user,
1215:                    String credentials) throws NamingException {
1216:
1217:                if (credentials == null || user == null)
1218:                    return (false);
1219:
1220:                String dn = user.dn;
1221:                if (dn == null)
1222:                    return (false);
1223:
1224:                // Validate the credentials specified by the user
1225:                if (containerLog.isTraceEnabled()) {
1226:                    containerLog
1227:                            .trace("  validating credentials by binding as the user");
1228:                }
1229:
1230:                // Set up security environment to bind as the user
1231:                context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
1232:                context.addToEnvironment(Context.SECURITY_CREDENTIALS,
1233:                        credentials);
1234:
1235:                // Elicit an LDAP bind operation
1236:                boolean validated = false;
1237:                try {
1238:                    if (containerLog.isTraceEnabled()) {
1239:                        containerLog.trace("  binding as " + dn);
1240:                    }
1241:                    context.getAttributes("", null);
1242:                    validated = true;
1243:                } catch (AuthenticationException e) {
1244:                    if (containerLog.isTraceEnabled()) {
1245:                        containerLog.trace("  bind attempt failed");
1246:                    }
1247:                }
1248:
1249:                // Restore the original security environment
1250:                if (connectionName != null) {
1251:                    context.addToEnvironment(Context.SECURITY_PRINCIPAL,
1252:                            connectionName);
1253:                } else {
1254:                    context.removeFromEnvironment(Context.SECURITY_PRINCIPAL);
1255:                }
1256:
1257:                if (connectionPassword != null) {
1258:                    context.addToEnvironment(Context.SECURITY_CREDENTIALS,
1259:                            connectionPassword);
1260:                } else {
1261:                    context.removeFromEnvironment(Context.SECURITY_CREDENTIALS);
1262:                }
1263:
1264:                return (validated);
1265:            }
1266:
1267:            /**
1268:             * Return a List of roles associated with the given User.  Any
1269:             * roles present in the user's directory entry are supplemented by
1270:             * a directory search. If no roles are associated with this user,
1271:             * a zero-length List is returned.
1272:             *
1273:             * @param context The directory context we are searching
1274:             * @param user The User to be checked
1275:             *
1276:             * @exception NamingException if a directory server error occurs
1277:             */
1278:            protected List<String> getRoles(DirContext context, User user)
1279:                    throws NamingException {
1280:
1281:                if (user == null)
1282:                    return (null);
1283:
1284:                String dn = user.dn;
1285:                String username = user.username;
1286:
1287:                if (dn == null || username == null)
1288:                    return (null);
1289:
1290:                if (containerLog.isTraceEnabled())
1291:                    containerLog.trace("  getRoles(" + dn + ")");
1292:
1293:                // Start with roles retrieved from the user entry
1294:                ArrayList<String> list = user.roles;
1295:                if (list == null) {
1296:                    list = new ArrayList<String>();
1297:                }
1298:
1299:                // Are we configured to do role searches?
1300:                if ((roleFormat == null) || (roleName == null))
1301:                    return (list);
1302:
1303:                // Set up parameters for an appropriate search
1304:                String filter = roleFormat.format(new String[] {
1305:                        doRFC2254Encoding(dn), username });
1306:                SearchControls controls = new SearchControls();
1307:                if (roleSubtree)
1308:                    controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
1309:                else
1310:                    controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
1311:                controls.setReturningAttributes(new String[] { roleName });
1312:
1313:                // Perform the configured search and process the results
1314:                NamingEnumeration results = context.search(roleBase, filter,
1315:                        controls);
1316:                if (results == null)
1317:                    return (list); // Should never happen, but just in case ...
1318:                while (results.hasMore()) {
1319:                    SearchResult result = (SearchResult) results.next();
1320:                    Attributes attrs = result.getAttributes();
1321:                    if (attrs == null)
1322:                        continue;
1323:                    list = addAttributeValues(roleName, attrs, list);
1324:                }
1325:
1326:                if (containerLog.isTraceEnabled()) {
1327:                    if (list != null) {
1328:                        containerLog.trace("  Returning " + list.size()
1329:                                + " roles");
1330:                        for (int i = 0; i < list.size(); i++)
1331:                            containerLog.trace("  Found role " + list.get(i));
1332:                    } else {
1333:                        containerLog.trace("  getRoles about to return null ");
1334:                    }
1335:                }
1336:
1337:                return (list);
1338:            }
1339:
1340:            /**
1341:             * Return a String representing the value of the specified attribute.
1342:             *
1343:             * @param attrId Attribute name
1344:             * @param attrs Attributes containing the required value
1345:             *
1346:             * @exception NamingException if a directory server error occurs
1347:             */
1348:            private String getAttributeValue(String attrId, Attributes attrs)
1349:                    throws NamingException {
1350:
1351:                if (containerLog.isTraceEnabled())
1352:                    containerLog.trace("  retrieving attribute " + attrId);
1353:
1354:                if (attrId == null || attrs == null)
1355:                    return null;
1356:
1357:                Attribute attr = attrs.get(attrId);
1358:                if (attr == null)
1359:                    return (null);
1360:                Object value = attr.get();
1361:                if (value == null)
1362:                    return (null);
1363:                String valueString = null;
1364:                if (value instanceof  byte[])
1365:                    valueString = new String((byte[]) value);
1366:                else
1367:                    valueString = value.toString();
1368:
1369:                return valueString;
1370:            }
1371:
1372:            /**
1373:             * Add values of a specified attribute to a list
1374:             *
1375:             * @param attrId Attribute name
1376:             * @param attrs Attributes containing the new values
1377:             * @param values ArrayList containing values found so far
1378:             *
1379:             * @exception NamingException if a directory server error occurs
1380:             */
1381:            private ArrayList<String> addAttributeValues(String attrId,
1382:                    Attributes attrs, ArrayList<String> values)
1383:                    throws NamingException {
1384:
1385:                if (containerLog.isTraceEnabled())
1386:                    containerLog.trace("  retrieving values for attribute "
1387:                            + attrId);
1388:                if (attrId == null || attrs == null)
1389:                    return values;
1390:                if (values == null)
1391:                    values = new ArrayList<String>();
1392:                Attribute attr = attrs.get(attrId);
1393:                if (attr == null)
1394:                    return (values);
1395:                NamingEnumeration e = attr.getAll();
1396:                while (e.hasMore()) {
1397:                    String value = (String) e.next();
1398:                    values.add(value);
1399:                }
1400:                return values;
1401:            }
1402:
1403:            /**
1404:             * Close any open connection to the directory server for this Realm.
1405:             *
1406:             * @param context The directory context to be closed
1407:             */
1408:            protected void close(DirContext context) {
1409:
1410:                // Do nothing if there is no opened connection
1411:                if (context == null)
1412:                    return;
1413:
1414:                // Close our opened connection
1415:                try {
1416:                    if (containerLog.isDebugEnabled())
1417:                        containerLog.debug("Closing directory context");
1418:                    context.close();
1419:                } catch (NamingException e) {
1420:                    containerLog.error(sm.getString("jndiRealm.close"), e);
1421:                }
1422:                this .context = null;
1423:
1424:            }
1425:
1426:            /**
1427:             * Return a short name for this Realm implementation.
1428:             */
1429:            protected String getName() {
1430:
1431:                return (name);
1432:
1433:            }
1434:
1435:            /**
1436:             * Return the password associated with the given principal's user name.
1437:             */
1438:            protected String getPassword(String username) {
1439:
1440:                return (null);
1441:
1442:            }
1443:
1444:            /**
1445:             * Return the Principal associated with the given user name.
1446:             */
1447:            protected Principal getPrincipal(String username) {
1448:
1449:                DirContext context = null;
1450:                Principal principal = null;
1451:
1452:                try {
1453:
1454:                    // Ensure that we have a directory context available
1455:                    context = open();
1456:
1457:                    // Occassionally the directory context will timeout.  Try one more
1458:                    // time before giving up.
1459:                    try {
1460:
1461:                        // Authenticate the specified username if possible
1462:                        principal = getPrincipal(context, username);
1463:
1464:                    } catch (CommunicationException e) {
1465:
1466:                        // log the exception so we know it's there.
1467:                        containerLog.warn(sm.getString("jndiRealm.exception"),
1468:                                e);
1469:
1470:                        // close the connection so we know it will be reopened.
1471:                        if (context != null)
1472:                            close(context);
1473:
1474:                        // open a new directory context.
1475:                        context = open();
1476:
1477:                        // Try the authentication again.
1478:                        principal = getPrincipal(context, username);
1479:
1480:                    }
1481:
1482:                    // Release this context
1483:                    release(context);
1484:
1485:                    // Return the authenticated Principal (if any)
1486:                    return (principal);
1487:
1488:                } catch (NamingException e) {
1489:
1490:                    // Log the problem for posterity
1491:                    containerLog.error(sm.getString("jndiRealm.exception"), e);
1492:
1493:                    // Close the connection so that it gets reopened next time
1494:                    if (context != null)
1495:                        close(context);
1496:
1497:                    // Return "not authenticated" for this request
1498:                    return (null);
1499:
1500:                }
1501:
1502:            }
1503:
1504:            /**
1505:             * Return the Principal associated with the given user name.
1506:             */
1507:            protected synchronized Principal getPrincipal(DirContext context,
1508:                    String username) throws NamingException {
1509:
1510:                User user = getUser(context, username);
1511:
1512:                return new GenericPrincipal(this , user.username, user.password,
1513:                        getRoles(context, user));
1514:            }
1515:
1516:            /**
1517:             * Open (if necessary) and return a connection to the configured
1518:             * directory server for this Realm.
1519:             *
1520:             * @exception NamingException if a directory server error occurs
1521:             */
1522:            protected DirContext open() throws NamingException {
1523:
1524:                // Do nothing if there is a directory server connection already open
1525:                if (context != null)
1526:                    return (context);
1527:
1528:                try {
1529:
1530:                    // Ensure that we have a directory context available
1531:                    context = new InitialDirContext(
1532:                            getDirectoryContextEnvironment());
1533:
1534:                } catch (Exception e) {
1535:
1536:                    connectionAttempt = 1;
1537:
1538:                    // log the first exception.
1539:                    containerLog.warn(sm.getString("jndiRealm.exception"), e);
1540:
1541:                    // Try connecting to the alternate url.
1542:                    context = new InitialDirContext(
1543:                            getDirectoryContextEnvironment());
1544:
1545:                } finally {
1546:
1547:                    // reset it in case the connection times out.
1548:                    // the primary may come back.
1549:                    connectionAttempt = 0;
1550:
1551:                }
1552:
1553:                return (context);
1554:
1555:            }
1556:
1557:            /**
1558:             * Create our directory context configuration.
1559:             *
1560:             * @return java.util.Hashtable the configuration for the directory context.
1561:             */
1562:            protected Hashtable getDirectoryContextEnvironment() {
1563:
1564:                Hashtable<String, String> env = new Hashtable<String, String>();
1565:
1566:                // Configure our directory context environment.
1567:                if (containerLog.isDebugEnabled() && connectionAttempt == 0)
1568:                    containerLog.debug("Connecting to URL " + connectionURL);
1569:                else if (containerLog.isDebugEnabled() && connectionAttempt > 0)
1570:                    containerLog.debug("Connecting to URL " + alternateURL);
1571:                env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
1572:                if (connectionName != null)
1573:                    env.put(Context.SECURITY_PRINCIPAL, connectionName);
1574:                if (connectionPassword != null)
1575:                    env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
1576:                if (connectionURL != null && connectionAttempt == 0)
1577:                    env.put(Context.PROVIDER_URL, connectionURL);
1578:                else if (alternateURL != null && connectionAttempt > 0)
1579:                    env.put(Context.PROVIDER_URL, alternateURL);
1580:                if (authentication != null)
1581:                    env.put(Context.SECURITY_AUTHENTICATION, authentication);
1582:                if (protocol != null)
1583:                    env.put(Context.SECURITY_PROTOCOL, protocol);
1584:                if (referrals != null)
1585:                    env.put(Context.REFERRAL, referrals);
1586:                if (derefAliases != null)
1587:                    env.put(JNDIRealm.DEREF_ALIASES, derefAliases);
1588:
1589:                return env;
1590:
1591:            }
1592:
1593:            /**
1594:             * Release our use of this connection so that it can be recycled.
1595:             *
1596:             * @param context The directory context to release
1597:             */
1598:            protected void release(DirContext context) {
1599:
1600:                ; // NO-OP since we are not pooling anything
1601:
1602:            }
1603:
1604:            // ------------------------------------------------------ Lifecycle Methods
1605:
1606:            /**
1607:             * Prepare for active use of the public methods of this Component.
1608:             *
1609:             * @exception LifecycleException if this component detects a fatal error
1610:             *  that prevents it from being started
1611:             */
1612:            public void start() throws LifecycleException {
1613:
1614:                // Perform normal superclass initialization
1615:                super .start();
1616:
1617:                // Validate that we can open our connection
1618:                try {
1619:                    open();
1620:                } catch (NamingException e) {
1621:                    throw new LifecycleException(
1622:                            sm.getString("jndiRealm.open"), e);
1623:                }
1624:
1625:            }
1626:
1627:            /**
1628:             * Gracefully shut down active use of the public methods of this Component.
1629:             *
1630:             * @exception LifecycleException if this component detects a fatal error
1631:             *  that needs to be reported
1632:             */
1633:            public void stop() throws LifecycleException {
1634:
1635:                // Perform normal superclass finalization
1636:                super .stop();
1637:
1638:                // Close any open directory server connection
1639:                close(this .context);
1640:
1641:            }
1642:
1643:            /**
1644:             * Given a string containing LDAP patterns for user locations (separated by
1645:             * parentheses in a pseudo-LDAP search string format -
1646:             * "(location1)(location2)", returns an array of those paths.  Real LDAP
1647:             * search strings are supported as well (though only the "|" "OR" type).
1648:             *
1649:             * @param userPatternString - a string LDAP search paths surrounded by
1650:             * parentheses
1651:             */
1652:            protected String[] parseUserPatternString(String userPatternString) {
1653:
1654:                if (userPatternString != null) {
1655:                    ArrayList<String> pathList = new ArrayList<String>();
1656:                    int startParenLoc = userPatternString.indexOf('(');
1657:                    if (startParenLoc == -1) {
1658:                        // no parens here; return whole thing
1659:                        return new String[] { userPatternString };
1660:                    }
1661:                    int startingPoint = 0;
1662:                    while (startParenLoc > -1) {
1663:                        int endParenLoc = 0;
1664:                        // weed out escaped open parens and parens enclosing the
1665:                        // whole statement (in the case of valid LDAP search
1666:                        // strings: (|(something)(somethingelse))
1667:                        while ((userPatternString.charAt(startParenLoc + 1) == '|')
1668:                                || (startParenLoc != 0 && userPatternString
1669:                                        .charAt(startParenLoc - 1) == '\\')) {
1670:                            startParenLoc = userPatternString.indexOf("(",
1671:                                    startParenLoc + 1);
1672:                        }
1673:                        endParenLoc = userPatternString.indexOf(")",
1674:                                startParenLoc + 1);
1675:                        // weed out escaped end-parens
1676:                        while (userPatternString.charAt(endParenLoc - 1) == '\\') {
1677:                            endParenLoc = userPatternString.indexOf(")",
1678:                                    endParenLoc + 1);
1679:                        }
1680:                        String nextPathPart = userPatternString.substring(
1681:                                startParenLoc + 1, endParenLoc);
1682:                        pathList.add(nextPathPart);
1683:                        startingPoint = endParenLoc + 1;
1684:                        startParenLoc = userPatternString.indexOf('(',
1685:                                startingPoint);
1686:                    }
1687:                    return (String[]) pathList.toArray(new String[] {});
1688:                }
1689:                return null;
1690:
1691:            }
1692:
1693:            /**
1694:             * Given an LDAP search string, returns the string with certain characters
1695:             * escaped according to RFC 2254 guidelines.
1696:             * The character mapping is as follows:
1697:             *     char ->  Replacement
1698:             *    ---------------------------
1699:             *     *  -> \2a
1700:             *     (  -> \28
1701:             *     )  -> \29
1702:             *     \  -> \5c
1703:             *     \0 -> \00
1704:             * @param inString string to escape according to RFC 2254 guidelines
1705:             * @return String the escaped/encoded result
1706:             */
1707:            protected String doRFC2254Encoding(String inString) {
1708:                StringBuffer buf = new StringBuffer(inString.length());
1709:                for (int i = 0; i < inString.length(); i++) {
1710:                    char c = inString.charAt(i);
1711:                    switch (c) {
1712:                    case '\\':
1713:                        buf.append("\\5c");
1714:                        break;
1715:                    case '*':
1716:                        buf.append("\\2a");
1717:                        break;
1718:                    case '(':
1719:                        buf.append("\\28");
1720:                        break;
1721:                    case ')':
1722:                        buf.append("\\29");
1723:                        break;
1724:                    case '\0':
1725:                        buf.append("\\00");
1726:                        break;
1727:                    default:
1728:                        buf.append(c);
1729:                        break;
1730:                    }
1731:                }
1732:                return buf.toString();
1733:            }
1734:
1735:        }
1736:
1737:        // ------------------------------------------------------ Private Classes
1738:
1739:        /**
1740:         * A private class representing a User
1741:         */
1742:        class User {
1743:            String username = null;
1744:            String dn = null;
1745:            String password = null;
1746:            ArrayList<String> roles = null;
1747:
1748:            User(String username, String dn, String password,
1749:                    ArrayList<String> roles) {
1750:                this.username = username;
1751:                this.dn = dn;
1752:                this.password = password;
1753:                this.roles = roles;
1754:            }
1755:
1756:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.