Source Code Cross Referenced for LdapLoginModule.java in  » 6.0-JDK-Modules-com.sun » security » com » sun » security » auth » module » 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 » 6.0 JDK Modules com.sun » security » com.sun.security.auth.module 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004:         *
0005:         * This code is free software; you can redistribute it and/or modify it
0006:         * under the terms of the GNU General Public License version 2 only, as
0007:         * published by the Free Software Foundation.  Sun designates this
0008:         * particular file as subject to the "Classpath" exception as provided
0009:         * by Sun in the LICENSE file that accompanied this code.
0010:         *
0011:         * This code is distributed in the hope that it will be useful, but WITHOUT
0012:         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013:         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014:         * version 2 for more details (a copy is included in the LICENSE file that
0015:         * accompanied this code).
0016:         *
0017:         * You should have received a copy of the GNU General Public License version
0018:         * 2 along with this work; if not, write to the Free Software Foundation,
0019:         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020:         *
0021:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022:         * CA 95054 USA or visit www.sun.com if you need additional information or
0023:         * have any questions.
0024:         */
0025:
0026:        package com.sun.security.auth.module;
0027:
0028:        import java.io.IOException;
0029:        import java.security.AccessController;
0030:        import java.net.SocketPermission;
0031:        import java.security.Principal;
0032:        import java.security.PrivilegedAction;
0033:        import java.util.Arrays;
0034:        import java.util.Hashtable;
0035:        import java.util.Iterator;
0036:        import java.util.Map;
0037:        import java.util.ResourceBundle;
0038:        import java.util.regex.Matcher;
0039:        import java.util.regex.Pattern;
0040:        import java.util.Set;
0041:
0042:        import javax.naming.*;
0043:        import javax.naming.directory.*;
0044:        import javax.naming.ldap.*;
0045:        import javax.security.auth.*;
0046:        import javax.security.auth.callback.*;
0047:        import javax.security.auth.login.*;
0048:        import javax.security.auth.spi.*;
0049:
0050:        import com.sun.security.auth.LdapPrincipal;
0051:        import com.sun.security.auth.UserPrincipal;
0052:
0053:        import sun.security.util.AuthResources;
0054:
0055:        /**
0056:         * This {@link LoginModule} performs LDAP-based authentication.
0057:         * A username and password is verified against the corresponding user
0058:         * credentials stored in an LDAP directory.
0059:         * This module requires the supplied {@link CallbackHandler} to support a
0060:         * {@link NameCallback} and a {@link PasswordCallback}.
0061:         * If authentication is successful then a new {@link LdapPrincipal} is created
0062:         * using the user's distinguished name and a new {@link UserPrincipal} is
0063:         * created using the user's username and both are associated
0064:         * with the current {@link Subject}.
0065:         *
0066:         * <p> This module operates in one of three modes: <i>search-first</i>,
0067:         * <i>authentication-first</i> or <i>authentication-only</i>.
0068:         * A mode is selected by specifying a particular set of options.
0069:         *
0070:         * <p> In search-first mode, the LDAP directory is searched to determine the
0071:         * user's distinguished name and then authentication is attempted.
0072:         * An (anonymous) search is performed using the supplied username in
0073:         * conjunction with a specified search filter.
0074:         * If successful then authentication is attempted using the user's
0075:         * distinguished name and the supplied password.
0076:         * To enable this mode, set the <code>userFilter</code> option and omit the
0077:         * <code>authIdentity</code> option.
0078:         * Use search-first mode when the user's distinguished name is not
0079:         * known in advance.
0080:         *
0081:         * <p> In authentication-first mode, authentication is attempted using the
0082:         * supplied username and password and then the LDAP directory is searched.
0083:         * If authentication is successful then a search is performed using the
0084:         * supplied username in conjunction with a specified search filter.
0085:         * To enable this mode, set the <code>authIdentity</code> and the
0086:         * <code>userFilter</code> options.
0087:         * Use authentication-first mode when accessing an LDAP directory
0088:         * that has been configured to disallow anonymous searches.
0089:         *
0090:         * <p> In authentication-only mode, authentication is attempted using the
0091:         * supplied username and password. The LDAP directory is not searched because
0092:         * the user's distinguished name is already known.
0093:         * To enable this mode, set the <code>authIdentity</code> option to a valid
0094:         * distinguished name and omit the <code>userFilter</code> option.
0095:         * Use authentication-only mode when the user's distinguished name is
0096:         * known in advance.
0097:         *
0098:         * <p> The following option is mandatory and must be specified in this
0099:         * module's login {@link Configuration}:
0100:         * <dl><dt></dt><dd>
0101:         * <dl>
0102:         * <dt> <code>userProvider=<b>ldap_urls</b></code>
0103:         * </dt>
0104:         * <dd> This option identifies the LDAP directory that stores user entries.
0105:         *      <b>ldap_urls</b> is a list of space-separated LDAP URLs
0106:         *      (<a href="http://www.ietf.org/rfc/rfc2255.txt">RFC 2255</a>)
0107:         *      that identifies the LDAP server to use and the position in
0108:         *      its directory tree where user entries are located.
0109:         *      When several LDAP URLs are specified then each is attempted,
0110:         *      in turn, until the first successful connection is established.
0111:         *      Spaces in the distinguished name component of the URL must be escaped
0112:         *      using the standard mechanism of percent character ('<code>%</code>')
0113:         *      followed by two hexadecimal digits (see {@link java.net.URI}).
0114:         *      Query components must also be omitted from the URL.
0115:         *
0116:         *      <p>
0117:         *      Automatic discovery of the LDAP server via DNS
0118:         *      (<a href="http://www.ietf.org/rfc/rfc2782.txt">RFC 2782</a>)
0119:         *      is supported (once DNS has been configured to support such a service).
0120:         *      It is enabled by omitting the hostname and port number components from
0121:         *      the LDAP URL. </dd>
0122:         * </dl></dl>
0123:         *
0124:         * <p> This module also recognizes the following optional {@link Configuration}
0125:         *     options:
0126:         * <dl><dt></dt><dd>
0127:         * <dl>
0128:         * <dt> <code>userFilter=<b>ldap_filter</b></code> </dt>
0129:         * <dd> This option specifies the search filter to use to locate a user's
0130:         *      entry in the LDAP directory. It is used to determine a user's
0131:         *      distinguished name.
0132:         *      <code><b>ldap_filter</b></code> is an LDAP filter string
0133:         *      (<a href="http://www.ietf.org/rfc/rfc2254.txt">RFC 2254</a>).
0134:         *      If it contains the special token "<code><b>{USERNAME}</b></code>"
0135:         *      then that token will be replaced with the supplied username value
0136:         *      before the filter is used to search the directory. </dd>
0137:         *
0138:         * <dt> <code>authIdentity=<b>auth_id</b></code> </dt>
0139:         * <dd> This option specifies the identity to use when authenticating a user
0140:         *      to the LDAP directory.
0141:         *      <code><b>auth_id</b></code> may be an LDAP distinguished name string
0142:         *      (<a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>) or some
0143:         *      other string name.
0144:         *      It must contain the special token "<code><b>{USERNAME}</b></code>"
0145:         *      which will be replaced with the supplied username value before the
0146:         *      name is used for authentication.
0147:         *      Note that if this option does not contain a distinguished name then
0148:         *      the <code>userFilter</code> option must also be specified. </dd>
0149:         *
0150:         * <dt> <code>authzIdentity=<b>authz_id</b></code> </dt>
0151:         * <dd> This option specifies an authorization identity for the user.
0152:         *      <code><b>authz_id</b></code> is any string name.
0153:         *      If it comprises a single special token with curly braces then
0154:         *      that token is treated as a attribute name and will be replaced with a
0155:         *      single value of that attribute from the user's LDAP entry.
0156:         *      If the attribute cannot be found then the option is ignored.
0157:         *      When this option is supplied and the user has been successfully
0158:         *      authenticated then an additional {@link UserPrincipal}
0159:         *      is created using the authorization identity and it is assocated with
0160:         *      the current {@link Subject}. </dd>
0161:         *
0162:         * <dt> <code>useSSL</code> </dt>
0163:         * <dd> if <code>false</code>, this module does not establish an SSL connection
0164:         *      to the LDAP server before attempting authentication. SSL is used to
0165:         *      protect the privacy of the user's password because it is transmitted
0166:         *      in the clear over LDAP.
0167:         *      By default, this module uses SSL. </dd>
0168:         *
0169:         * <dt> <code>useFirstPass</code> </dt>
0170:         * <dd> if <code>true</code>, this module retrieves the username and password
0171:         *      from the module's shared state, using "javax.security.auth.login.name"
0172:         *      and "javax.security.auth.login.password" as the respective keys. The
0173:         *      retrieved values are used for authentication. If authentication fails,
0174:         *      no attempt for a retry is made, and the failure is reported back to
0175:         *      the calling application.</dd>
0176:         *
0177:         * <dt> <code>tryFirstPass</code> </dt>
0178:         * <dd> if <code>true</code>, this module retrieves the username and password
0179:         *      from the module's shared state, using "javax.security.auth.login.name"
0180:         *       and "javax.security.auth.login.password" as the respective keys.  The
0181:         *      retrieved values are used for authentication. If authentication fails,
0182:         *      the module uses the {@link CallbackHandler} to retrieve a new username
0183:         *      and password, and another attempt to authenticate is made. If the
0184:         *      authentication fails, the failure is reported back to the calling
0185:         *      application.</dd>
0186:         *
0187:         * <dt> <code>storePass</code> </dt>
0188:         * <dd> if <code>true</code>, this module stores the username and password
0189:         *      obtained from the {@link CallbackHandler} in the module's shared state,
0190:         *      using
0191:         *      "javax.security.auth.login.name" and
0192:         *      "javax.security.auth.login.password" as the respective keys.  This is
0193:         *      not performed if existing values already exist for the username and
0194:         *      password in the shared state, or if authentication fails.</dd>
0195:         *
0196:         * <dt> <code>clearPass</code> </dt>
0197:         * <dd> if <code>true</code>, this module clears the username and password
0198:         *      stored in the module's shared state after both phases of authentication
0199:         *      (login and commit) have completed.</dd>
0200:         *
0201:         * <dt> <code>debug</code> </dt>
0202:         * <dd> if <code>true</code>, debug messages are displayed on the standard
0203:         *      output stream.
0204:         * </dl>
0205:         * </dl>
0206:         *
0207:         * <p>
0208:         * Arbitrary
0209:         * <a href="{@docRoot}/../../../../../technotes/guides/jndi/jndi-ldap-gl.html#PROP">JNDI properties</a>
0210:         * may also be specified in the {@link Configuration}.
0211:         * They are added to the environment and passed to the LDAP provider.
0212:         * Note that the following four JNDI properties are set by this module directly
0213:         * and are ignored if also present in the configuration:
0214:         * <ul>
0215:         * <li> <code>java.naming.provider.url</code>
0216:         * <li> <code>java.naming.security.principal</code>
0217:         * <li> <code>java.naming.security.credentials</code>
0218:         * <li> <code>java.naming.security.protocol</code>
0219:         * </ul>
0220:         *
0221:         * <p>
0222:         * Three sample {@link Configuration}s are shown below.
0223:         * The first one activates search-first mode. It identifies the LDAP server
0224:         * and specifies that users' entries be located by their <code>uid</code> and
0225:         * <code>objectClass</code> attributes. It also specifies that an identity
0226:         * based on the user's <code>employeeNumber</code> attribute should be created.
0227:         * The second one activates authentication-first mode. It requests that the
0228:         * LDAP server be located dynamically, that authentication be performed using
0229:         * the supplied username directly but without the protection of SSL and that
0230:         * users' entries be located by one of three naming attributes and their
0231:         * <code>objectClass</code> attribute.
0232:         * The third one activates authentication-only mode. It identifies alternative
0233:         * LDAP servers, it specifies the distinguished name to use for
0234:         * authentication and a fixed identity to use for authorization. No directory
0235:         * search is performed.
0236:         *
0237:         * <pre>
0238:         *
0239:         *     ExampleApplication {
0240:         *         com.sun.security.auth.module.LdapLoginModule REQUIRED
0241:         *             userProvider="ldap://ldap-svr/ou=people,dc=example,dc=com"
0242:         *             userFilter="(&(uid={USERNAME})(objectClass=inetOrgPerson))"
0243:         *             authzIdentity="{EMPLOYEENUMBER}"
0244:         *             debug=true;
0245:         *     };
0246:         *
0247:         *     ExampleApplication {
0248:         *         com.sun.security.auth.module.LdapLoginModule REQUIRED
0249:         *             userProvider="ldap:///cn=users,dc=example,dc=com"
0250:         *             authIdentity="{USERNAME}"
0251:         *             userFilter="(&(|(samAccountName={USERNAME})(userPrincipalName={USERNAME})(cn={USERNAME}))(objectClass=user))"
0252:         *             useSSL=false
0253:         *             debug=true;
0254:         *     };
0255:         *
0256:         *     ExampleApplication {
0257:         *         com.sun.security.auth.module.LdapLoginModule REQUIRED
0258:         *             userProvider="ldap://ldap-svr1 ldap://ldap-svr2"
0259:         *             authIdentity="cn={USERNAME},ou=people,dc=example,dc=com"
0260:         *             authzIdentity="staff"
0261:         *             debug=true;
0262:         *     };
0263:         *
0264:         * </pre>
0265:         *
0266:         * <dl>
0267:         * <dt><b>Note:</b> </dt>
0268:         * <dd>When a {@link SecurityManager} is active then an application
0269:         *     that creates a {@link LoginContext} and uses a {@link LoginModule}
0270:         *     must be granted certain permissions.
0271:         *     <p>
0272:         *     If the application creates a login context using an <em>installed</em>
0273:         *     {@link Configuration} then the application must be granted the
0274:         *     {@link AuthPermission} to create login contexts.
0275:         *     For example, the following security policy allows an application in
0276:         *     the user's current directory to instantiate <em>any</em> login context:
0277:         *     <pre>
0278:         *
0279:         *     grant codebase "file:${user.dir}/" {
0280:         *         permission javax.security.auth.AuthPermission "createLoginContext.*";
0281:         *     };
0282:         *     </pre>
0283:         *
0284:         *     Alternatively, if the application creates a login context using a
0285:         *     <em>caller-specified</em> {@link Configuration} then the application
0286:         *     must be granted the permissions required by the {@link LoginModule}.
0287:         *     <em>This</em> module requires the following two permissions:
0288:         *     <p>
0289:         *     <ul>
0290:         *     <li> The {@link SocketPermission} to connect to an LDAP server.
0291:         *     <li> The {@link AuthPermission} to modify the set of {@link Principal}s
0292:         *          associated with a {@link Subject}.
0293:         *     </ul>
0294:         *     <p>
0295:         *     For example, the following security policy grants an application in the
0296:         *     user's current directory all the permissions required by this module:
0297:         *     <pre>
0298:         *
0299:         *     grant codebase "file:${user.dir}/" {
0300:         *         permission java.net.SocketPermission "*:389", "connect";
0301:         *         permission java.net.SocketPermission "*:636", "connect";
0302:         *         permission javax.security.auth.AuthPermission "modifyPrincipals";
0303:         *     };
0304:         *     </pre>
0305:         * </dd>
0306:         * </dl>
0307:         *
0308:         * @since 1.6
0309:         */
0310:        public class LdapLoginModule implements  LoginModule {
0311:
0312:            // Use the default classloader for this class to load the prompt strings.
0313:            private static final ResourceBundle rb = AccessController
0314:                    .doPrivileged(new PrivilegedAction<ResourceBundle>() {
0315:                        public ResourceBundle run() {
0316:                            return ResourceBundle
0317:                                    .getBundle("sun.security.util.AuthResources");
0318:                        }
0319:                    });
0320:
0321:            // Keys to retrieve the stored username and password
0322:            private static final String USERNAME_KEY = "javax.security.auth.login.name";
0323:            private static final String PASSWORD_KEY = "javax.security.auth.login.password";
0324:
0325:            // Option names
0326:            private static final String USER_PROVIDER = "userProvider";
0327:            private static final String USER_FILTER = "userFilter";
0328:            private static final String AUTHC_IDENTITY = "authIdentity";
0329:            private static final String AUTHZ_IDENTITY = "authzIdentity";
0330:
0331:            // Used for the username token replacement
0332:            private static final String USERNAME_TOKEN = "{USERNAME}";
0333:            private static final Pattern USERNAME_PATTERN = Pattern
0334:                    .compile("\\{USERNAME\\}");
0335:
0336:            // Configurable options
0337:            private String userProvider;
0338:            private String userFilter;
0339:            private String authcIdentity;
0340:            private String authzIdentity;
0341:            private String authzIdentityAttr = null;
0342:            private boolean useSSL = true;
0343:            private boolean authFirst = false;
0344:            private boolean authOnly = false;
0345:            private boolean useFirstPass = false;
0346:            private boolean tryFirstPass = false;
0347:            private boolean storePass = false;
0348:            private boolean clearPass = false;
0349:            private boolean debug = false;
0350:
0351:            // Authentication status
0352:            private boolean succeeded = false;
0353:            private boolean commitSucceeded = false;
0354:
0355:            // Supplied username and password
0356:            private String username;
0357:            private char[] password;
0358:
0359:            // User's identities
0360:            private LdapPrincipal ldapPrincipal;
0361:            private UserPrincipal userPrincipal;
0362:            private UserPrincipal authzPrincipal;
0363:
0364:            // Initial state
0365:            private Subject subject;
0366:            private CallbackHandler callbackHandler;
0367:            private Map sharedState;
0368:            private Map<String, ?> options;
0369:            private LdapContext ctx;
0370:            private Matcher identityMatcher = null;
0371:            private Matcher filterMatcher = null;
0372:            private Hashtable ldapEnvironment;
0373:            private SearchControls constraints = null;
0374:
0375:            /**
0376:             * Initialize this <code>LoginModule</code>.
0377:             *
0378:             * @param subject the <code>Subject</code> to be authenticated.
0379:             * @param callbackHandler a <code>CallbackHandler</code> to acquire the
0380:             *                  username and password.
0381:             * @param sharedState shared <code>LoginModule</code> state.
0382:             * @param options options specified in the login
0383:             *			<code>Configuration</code> for this particular
0384:             *			<code>LoginModule</code>.
0385:             */
0386:            public void initialize(Subject subject,
0387:                    CallbackHandler callbackHandler,
0388:                    Map<String, ?> sharedState, Map<String, ?> options) {
0389:
0390:                this .subject = subject;
0391:                this .callbackHandler = callbackHandler;
0392:                this .sharedState = sharedState;
0393:                this .options = options;
0394:
0395:                ldapEnvironment = new Hashtable(9);
0396:                ldapEnvironment.put(Context.INITIAL_CONTEXT_FACTORY,
0397:                        "com.sun.jndi.ldap.LdapCtxFactory");
0398:
0399:                // Add any JNDI properties to the environment
0400:                for (String key : options.keySet()) {
0401:                    if (key.indexOf(".") > -1) {
0402:                        ldapEnvironment.put(key, options.get(key));
0403:                    }
0404:                }
0405:
0406:                // initialize any configured options
0407:
0408:                userProvider = (String) options.get(USER_PROVIDER);
0409:                if (userProvider != null) {
0410:                    ldapEnvironment.put(Context.PROVIDER_URL, userProvider);
0411:                }
0412:
0413:                authcIdentity = (String) options.get(AUTHC_IDENTITY);
0414:                if (authcIdentity != null
0415:                        && (authcIdentity.indexOf(USERNAME_TOKEN) != -1)) {
0416:                    identityMatcher = USERNAME_PATTERN.matcher(authcIdentity);
0417:                }
0418:
0419:                userFilter = (String) options.get(USER_FILTER);
0420:                if (userFilter != null) {
0421:                    if (userFilter.indexOf(USERNAME_TOKEN) != -1) {
0422:                        filterMatcher = USERNAME_PATTERN.matcher(userFilter);
0423:                    }
0424:                    constraints = new SearchControls();
0425:                    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
0426:                    constraints.setReturningAttributes(new String[0]); //return no attrs
0427:                    constraints.setReturningObjFlag(true); // to get the full DN
0428:                }
0429:
0430:                authzIdentity = (String) options.get(AUTHZ_IDENTITY);
0431:                if (authzIdentity != null && authzIdentity.startsWith("{")
0432:                        && authzIdentity.endsWith("}")) {
0433:                    if (constraints != null) {
0434:                        authzIdentityAttr = authzIdentity.substring(1,
0435:                                authzIdentity.length() - 1);
0436:                        constraints
0437:                                .setReturningAttributes(new String[] { authzIdentityAttr });
0438:                    }
0439:                    authzIdentity = null; // set later, from the specified attribute
0440:                }
0441:
0442:                // determine mode
0443:                if (authcIdentity != null) {
0444:                    if (userFilter != null) {
0445:                        authFirst = true; // authentication-first mode
0446:                    } else {
0447:                        authOnly = true; // authentication-only mode
0448:                    }
0449:                }
0450:
0451:                if ("false".equalsIgnoreCase((String) options.get("useSSL"))) {
0452:                    useSSL = false;
0453:                    ldapEnvironment.remove(Context.SECURITY_PROTOCOL);
0454:                } else {
0455:                    ldapEnvironment.put(Context.SECURITY_PROTOCOL, "ssl");
0456:                }
0457:
0458:                tryFirstPass = "true".equalsIgnoreCase((String) options
0459:                        .get("tryFirstPass"));
0460:
0461:                useFirstPass = "true".equalsIgnoreCase((String) options
0462:                        .get("useFirstPass"));
0463:
0464:                storePass = "true".equalsIgnoreCase((String) options
0465:                        .get("storePass"));
0466:
0467:                clearPass = "true".equalsIgnoreCase((String) options
0468:                        .get("clearPass"));
0469:
0470:                debug = "true".equalsIgnoreCase((String) options.get("debug"));
0471:
0472:                if (debug) {
0473:                    if (authFirst) {
0474:                        System.out.println("\t\t[LdapLoginModule] "
0475:                                + "authentication-first mode; "
0476:                                + (useSSL ? "SSL enabled" : "SSL disabled"));
0477:                    } else if (authOnly) {
0478:                        System.out.println("\t\t[LdapLoginModule] "
0479:                                + "authentication-only mode; "
0480:                                + (useSSL ? "SSL enabled" : "SSL disabled"));
0481:                    } else {
0482:                        System.out.println("\t\t[LdapLoginModule] "
0483:                                + "search-first mode; "
0484:                                + (useSSL ? "SSL enabled" : "SSL disabled"));
0485:                    }
0486:                }
0487:            }
0488:
0489:            /**
0490:             * Begin user authentication.
0491:             *
0492:             * <p> Acquire the user's credentials and verify them against the
0493:             * specified LDAP directory.
0494:             *
0495:             * @return true always, since this <code>LoginModule</code>
0496:             *		should not be ignored.
0497:             * @exception FailedLoginException if the authentication fails.
0498:             * @exception LoginException if this <code>LoginModule</code>
0499:             *		is unable to perform the authentication.
0500:             */
0501:            public boolean login() throws LoginException {
0502:
0503:                if (userProvider == null) {
0504:                    throw new LoginException(
0505:                            "Unable to locate the LDAP directory service");
0506:                }
0507:
0508:                if (debug) {
0509:                    System.out.println("\t\t[LdapLoginModule] user provider: "
0510:                            + userProvider);
0511:                }
0512:
0513:                // attempt the authentication
0514:                if (tryFirstPass) {
0515:
0516:                    try {
0517:                        // attempt the authentication by getting the
0518:                        // username and password from shared state
0519:                        attemptAuthentication(true);
0520:
0521:                        // authentication succeeded
0522:                        succeeded = true;
0523:                        if (debug) {
0524:                            System.out.println("\t\t[LdapLoginModule] "
0525:                                    + "tryFirstPass succeeded");
0526:                        }
0527:                        return true;
0528:
0529:                    } catch (LoginException le) {
0530:                        // authentication failed -- try again below by prompting
0531:                        cleanState();
0532:                        if (debug) {
0533:                            System.out.println("\t\t[LdapLoginModule] "
0534:                                    + "tryFirstPass failed: " + le.toString());
0535:                        }
0536:                    }
0537:
0538:                } else if (useFirstPass) {
0539:
0540:                    try {
0541:                        // attempt the authentication by getting the
0542:                        // username and password from shared state
0543:                        attemptAuthentication(true);
0544:
0545:                        // authentication succeeded
0546:                        succeeded = true;
0547:                        if (debug) {
0548:                            System.out.println("\t\t[LdapLoginModule] "
0549:                                    + "useFirstPass succeeded");
0550:                        }
0551:                        return true;
0552:
0553:                    } catch (LoginException le) {
0554:                        // authentication failed
0555:                        cleanState();
0556:                        if (debug) {
0557:                            System.out.println("\t\t[LdapLoginModule] "
0558:                                    + "useFirstPass failed");
0559:                        }
0560:                        throw le;
0561:                    }
0562:                }
0563:
0564:                // attempt the authentication by prompting for the username and pwd
0565:                try {
0566:                    attemptAuthentication(false);
0567:
0568:                    // authentication succeeded
0569:                    succeeded = true;
0570:                    if (debug) {
0571:                        System.out.println("\t\t[LdapLoginModule] "
0572:                                + "authentication succeeded");
0573:                    }
0574:                    return true;
0575:
0576:                } catch (LoginException le) {
0577:                    cleanState();
0578:                    if (debug) {
0579:                        System.out.println("\t\t[LdapLoginModule] "
0580:                                + "authentication failed");
0581:                    }
0582:                    throw le;
0583:                }
0584:            }
0585:
0586:            /**
0587:             * Complete user authentication.
0588:             *
0589:             * <p> This method is called if the LoginContext's
0590:             * overall authentication succeeded
0591:             * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
0592:             * succeeded).
0593:             *
0594:             * <p> If this LoginModule's own authentication attempt
0595:             * succeeded (checked by retrieving the private state saved by the
0596:             * <code>login</code> method), then this method associates an
0597:             * <code>LdapPrincipal</code> and one or more <code>UserPrincipal</code>s
0598:             * with the <code>Subject</code> located in the
0599:             * <code>LoginModule</code>.  If this LoginModule's own
0600:             * authentication attempted failed, then this method removes
0601:             * any state that was originally saved.
0602:             *
0603:             * @exception LoginException if the commit fails
0604:             * @return true if this LoginModule's own login and commit
0605:             *		attempts succeeded, or false otherwise.
0606:             */
0607:            public boolean commit() throws LoginException {
0608:
0609:                if (succeeded == false) {
0610:                    return false;
0611:                } else {
0612:                    if (subject.isReadOnly()) {
0613:                        cleanState();
0614:                        throw new LoginException("Subject is read-only");
0615:                    }
0616:                    // add Principals to the Subject
0617:                    Set<Principal> principals = subject.getPrincipals();
0618:                    if (!principals.contains(ldapPrincipal)) {
0619:                        principals.add(ldapPrincipal);
0620:                    }
0621:                    if (debug) {
0622:                        System.out.println("\t\t[LdapLoginModule] "
0623:                                + "added LdapPrincipal \"" + ldapPrincipal
0624:                                + "\" to Subject");
0625:                    }
0626:
0627:                    if (!principals.contains(userPrincipal)) {
0628:                        principals.add(userPrincipal);
0629:                    }
0630:                    if (debug) {
0631:                        System.out.println("\t\t[LdapLoginModule] "
0632:                                + "added UserPrincipal \"" + userPrincipal
0633:                                + "\" to Subject");
0634:                    }
0635:
0636:                    if (authzPrincipal != null
0637:                            && (!principals.contains(authzPrincipal))) {
0638:                        principals.add(authzPrincipal);
0639:
0640:                        if (debug) {
0641:                            System.out.println("\t\t[LdapLoginModule] "
0642:                                    + "added UserPrincipal \"" + authzPrincipal
0643:                                    + "\" to Subject");
0644:                        }
0645:                    }
0646:                }
0647:                // in any case, clean out state
0648:                cleanState();
0649:                commitSucceeded = true;
0650:                return true;
0651:            }
0652:
0653:            /**
0654:             * Abort user authentication.
0655:             *
0656:             * <p> This method is called if the overall authentication failed.
0657:             * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
0658:             * did not succeed).
0659:             *
0660:             * <p> If this LoginModule's own authentication attempt
0661:             * succeeded (checked by retrieving the private state saved by the
0662:             * <code>login</code> and <code>commit</code> methods),
0663:             * then this method cleans up any state that was originally saved.
0664:             *
0665:             * @exception LoginException if the abort fails.
0666:             * @return false if this LoginModule's own login and/or commit attempts
0667:             *		failed, and true otherwise.
0668:             */
0669:            public boolean abort() throws LoginException {
0670:                if (debug)
0671:                    System.out.println("\t\t[LdapLoginModule] "
0672:                            + "aborted authentication");
0673:
0674:                if (succeeded == false) {
0675:                    return false;
0676:                } else if (succeeded == true && commitSucceeded == false) {
0677:
0678:                    // Clean out state
0679:                    succeeded = false;
0680:                    cleanState();
0681:
0682:                    ldapPrincipal = null;
0683:                    userPrincipal = null;
0684:                    authzPrincipal = null;
0685:                } else {
0686:                    // overall authentication succeeded and commit succeeded,
0687:                    // but someone else's commit failed
0688:                    logout();
0689:                }
0690:                return true;
0691:            }
0692:
0693:            /**
0694:             * Logout a user.
0695:             *
0696:             * <p> This method removes the Principals
0697:             * that were added by the <code>commit</code> method.
0698:             *
0699:             * @exception LoginException if the logout fails.
0700:             * @return true in all cases since this <code>LoginModule</code>
0701:             *		should not be ignored.
0702:             */
0703:            public boolean logout() throws LoginException {
0704:                if (subject.isReadOnly()) {
0705:                    cleanState();
0706:                    throw new LoginException("Subject is read-only");
0707:                }
0708:                Set<Principal> principals = subject.getPrincipals();
0709:                principals.remove(ldapPrincipal);
0710:                principals.remove(userPrincipal);
0711:                if (authzIdentity != null) {
0712:                    principals.remove(authzPrincipal);
0713:                }
0714:
0715:                // clean out state
0716:                cleanState();
0717:                succeeded = false;
0718:                commitSucceeded = false;
0719:
0720:                ldapPrincipal = null;
0721:                userPrincipal = null;
0722:                authzPrincipal = null;
0723:
0724:                if (debug) {
0725:                    System.out
0726:                            .println("\t\t[LdapLoginModule] logged out Subject");
0727:                }
0728:                return true;
0729:            }
0730:
0731:            /**
0732:             * Attempt authentication
0733:             *
0734:             * @param getPasswdFromSharedState boolean that tells this method whether
0735:             *		to retrieve the password from the sharedState.
0736:             * @exception LoginException if the authentication attempt fails.
0737:             */
0738:            private void attemptAuthentication(boolean getPasswdFromSharedState)
0739:                    throws LoginException {
0740:
0741:                // first get the username and password
0742:                getUsernamePassword(getPasswdFromSharedState);
0743:
0744:                if (password == null || password.length == 0) {
0745:                    throw (LoginException) new FailedLoginException(
0746:                            "No password was supplied");
0747:                }
0748:
0749:                String dn = "";
0750:
0751:                if (authFirst || authOnly) {
0752:
0753:                    String id = replaceUsernameToken(identityMatcher,
0754:                            authcIdentity);
0755:
0756:                    // Prepare to bind using user's username and password
0757:                    ldapEnvironment.put(Context.SECURITY_CREDENTIALS, password);
0758:                    ldapEnvironment.put(Context.SECURITY_PRINCIPAL, id);
0759:
0760:                    if (debug) {
0761:                        System.out.println("\t\t[LdapLoginModule] "
0762:                                + "attempting to authenticate user: "
0763:                                + username);
0764:                    }
0765:
0766:                    try {
0767:                        // Connect to the LDAP server (using simple bind)
0768:                        ctx = new InitialLdapContext(ldapEnvironment, null);
0769:
0770:                    } catch (NamingException e) {
0771:                        throw (LoginException) new FailedLoginException(
0772:                                "Cannot bind to LDAP server").initCause(e);
0773:                    }
0774:
0775:                    // Authentication has succeeded
0776:
0777:                    // Locate the user's distinguished name
0778:                    if (userFilter != null) {
0779:                        dn = findUserDN(ctx);
0780:                    } else {
0781:                        dn = id;
0782:                    }
0783:
0784:                } else {
0785:
0786:                    try {
0787:                        // Connect to the LDAP server (using anonymous bind)
0788:                        ctx = new InitialLdapContext(ldapEnvironment, null);
0789:
0790:                    } catch (NamingException e) {
0791:                        throw (LoginException) new FailedLoginException(
0792:                                "Cannot connect to LDAP server").initCause(e);
0793:                    }
0794:
0795:                    // Locate the user's distinguished name
0796:                    dn = findUserDN(ctx);
0797:
0798:                    try {
0799:
0800:                        // Prepare to bind using user's distinguished name and password
0801:                        ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,
0802:                                "simple");
0803:                        ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
0804:                        ctx.addToEnvironment(Context.SECURITY_CREDENTIALS,
0805:                                password);
0806:
0807:                        if (debug) {
0808:                            System.out.println("\t\t[LdapLoginModule] "
0809:                                    + "attempting to authenticate user: "
0810:                                    + username);
0811:                        }
0812:                        // Connect to the LDAP server (using simple bind)
0813:                        ctx.reconnect(null);
0814:
0815:                        // Authentication has succeeded
0816:
0817:                    } catch (NamingException e) {
0818:                        throw (LoginException) new FailedLoginException(
0819:                                "Cannot bind to LDAP server").initCause(e);
0820:                    }
0821:                }
0822:
0823:                // Save input as shared state only if authentication succeeded
0824:                if (storePass && !sharedState.containsKey(USERNAME_KEY)
0825:                        && !sharedState.containsKey(PASSWORD_KEY)) {
0826:                    sharedState.put(USERNAME_KEY, username);
0827:                    sharedState.put(PASSWORD_KEY, password);
0828:                }
0829:
0830:                // Create the user principals
0831:                userPrincipal = new UserPrincipal(username);
0832:                if (authzIdentity != null) {
0833:                    authzPrincipal = new UserPrincipal(authzIdentity);
0834:                }
0835:
0836:                try {
0837:
0838:                    ldapPrincipal = new LdapPrincipal(dn);
0839:
0840:                } catch (InvalidNameException e) {
0841:                    if (debug) {
0842:                        System.out.println("\t\t[LdapLoginModule] "
0843:                                + "cannot create LdapPrincipal: bad DN");
0844:                    }
0845:                    throw (LoginException) new FailedLoginException(
0846:                            "Cannot create LdapPrincipal").initCause(e);
0847:                }
0848:            }
0849:
0850:            /**
0851:             * Search for the user's entry.
0852:             * Determine the distinguished name of the user's entry and optionally
0853:             * an authorization identity for the user.
0854:             *
0855:             * @param ctx an LDAP context to use for the search
0856:             * @return the user's distinguished name or an empty string if none
0857:             *         was found.
0858:             * @exception LoginException if the user's entry cannot be found.
0859:             */
0860:            private String findUserDN(LdapContext ctx) throws LoginException {
0861:
0862:                String userDN = "";
0863:
0864:                // Locate the user's LDAP entry
0865:                if (userFilter != null) {
0866:                    if (debug) {
0867:                        System.out.println("\t\t[LdapLoginModule] "
0868:                                + "searching for entry belonging to user: "
0869:                                + username);
0870:                    }
0871:                } else {
0872:                    if (debug) {
0873:                        System.out.println("\t\t[LdapLoginModule] "
0874:                                + "cannot search for entry belonging to user: "
0875:                                + username);
0876:                    }
0877:                    throw (LoginException) new FailedLoginException(
0878:                            "Cannot find user's LDAP entry");
0879:                }
0880:
0881:                try {
0882:                    NamingEnumeration<SearchResult> results = ctx.search("",
0883:                            replaceUsernameToken(filterMatcher, userFilter),
0884:                            constraints);
0885:
0886:                    // Extract the distinguished name of the user's entry
0887:                    // (Use the first entry if more than one is returned)
0888:                    if (results.hasMore()) {
0889:                        SearchResult entry = results.next();
0890:
0891:                        // %%% - use the SearchResult.getNameInNamespace method
0892:                        //        available in JDK 1.5 and later.
0893:                        //        (can remove call to constraints.setReturningObjFlag)
0894:                        userDN = ((Context) entry.getObject())
0895:                                .getNameInNamespace();
0896:
0897:                        if (debug) {
0898:                            System.out
0899:                                    .println("\t\t[LdapLoginModule] found entry: "
0900:                                            + userDN);
0901:                        }
0902:
0903:                        // Extract a value from user's authorization identity attribute
0904:                        if (authzIdentityAttr != null) {
0905:                            Attribute attr = entry.getAttributes().get(
0906:                                    authzIdentityAttr);
0907:                            if (attr != null) {
0908:                                Object val = attr.get();
0909:                                if (val instanceof  String) {
0910:                                    authzIdentity = (String) val;
0911:                                }
0912:                            }
0913:                        }
0914:
0915:                        results.close();
0916:
0917:                    } else {
0918:                        // Bad username
0919:                        if (debug) {
0920:                            System.out
0921:                                    .println("\t\t[LdapLoginModule] user's entry "
0922:                                            + "not found");
0923:                        }
0924:                    }
0925:
0926:                } catch (NamingException e) {
0927:                    // ignore
0928:                }
0929:
0930:                if (userDN.equals("")) {
0931:                    throw (LoginException) new FailedLoginException(
0932:                            "Cannot find user's LDAP entry");
0933:                } else {
0934:                    return userDN;
0935:                }
0936:            }
0937:
0938:            /**
0939:             * Replace the username token
0940:             *
0941:             * @param string the target string
0942:             * @return the modified string
0943:             */
0944:            private String replaceUsernameToken(Matcher matcher, String string) {
0945:                return matcher != null ? matcher.replaceAll(username) : string;
0946:            }
0947:
0948:            /**
0949:             * Get the username and password.
0950:             * This method does not return any value.
0951:             * Instead, it sets global name and password variables.
0952:             *
0953:             * <p> Also note that this method will set the username and password
0954:             * values in the shared state in case subsequent LoginModules
0955:             * want to use them via use/tryFirstPass.
0956:             *
0957:             * @param getPasswdFromSharedState boolean that tells this method whether
0958:             *		to retrieve the password from the sharedState.
0959:             * @exception LoginException if the username/password cannot be acquired.
0960:             */
0961:            private void getUsernamePassword(boolean getPasswdFromSharedState)
0962:                    throws LoginException {
0963:
0964:                if (getPasswdFromSharedState) {
0965:                    // use the password saved by the first module in the stack
0966:                    username = (String) sharedState.get(USERNAME_KEY);
0967:                    password = (char[]) sharedState.get(PASSWORD_KEY);
0968:                    return;
0969:                }
0970:
0971:                // prompt for a username and password
0972:                if (callbackHandler == null)
0973:                    throw new LoginException(
0974:                            "No CallbackHandler available "
0975:                                    + "to acquire authentication information from the user");
0976:
0977:                Callback[] callbacks = new Callback[2];
0978:                callbacks[0] = new NameCallback(rb.getString("username: "));
0979:                callbacks[1] = new PasswordCallback(rb.getString("password: "),
0980:                        false);
0981:
0982:                try {
0983:                    callbackHandler.handle(callbacks);
0984:                    username = ((NameCallback) callbacks[0]).getName();
0985:                    char[] tmpPassword = ((PasswordCallback) callbacks[1])
0986:                            .getPassword();
0987:                    password = new char[tmpPassword.length];
0988:                    System.arraycopy(tmpPassword, 0, password, 0,
0989:                            tmpPassword.length);
0990:                    ((PasswordCallback) callbacks[1]).clearPassword();
0991:
0992:                } catch (java.io.IOException ioe) {
0993:                    throw new LoginException(ioe.toString());
0994:
0995:                } catch (UnsupportedCallbackException uce) {
0996:                    throw new LoginException(
0997:                            "Error: "
0998:                                    + uce.getCallback().toString()
0999:                                    + " not available to acquire authentication information"
1000:                                    + " from the user");
1001:                }
1002:            }
1003:
1004:            /**
1005:             * Clean out state because of a failed authentication attempt
1006:             */
1007:            private void cleanState() {
1008:                username = null;
1009:                if (password != null) {
1010:                    Arrays.fill(password, ' ');
1011:                    password = null;
1012:                }
1013:                try {
1014:                    if (ctx != null) {
1015:                        ctx.close();
1016:                    }
1017:                } catch (NamingException e) {
1018:                    // ignore
1019:                }
1020:                ctx = null;
1021:
1022:                if (clearPass) {
1023:                    sharedState.remove(USERNAME_KEY);
1024:                    sharedState.remove(PASSWORD_KEY);
1025:                }
1026:            }
1027:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.