001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.realm.ldap;
020
021 import java.util.Hashtable;
022 import java.util.Map;
023 import javax.naming.Context;
024 import javax.naming.NamingException;
025 import javax.naming.ldap.InitialLdapContext;
026 import javax.naming.ldap.LdapContext;
027
028 import org.slf4j.Logger;
029 import org.slf4j.LoggerFactory;
030
031 /**
032 * <p>Default implementation of {@link LdapContextFactory} that can be configured or extended to
033 * customize the way {@link javax.naming.ldap.LdapContext} objects are retrieved.</p>
034 *
035 * <p>This implementation of {@link LdapContextFactory} is used by the {@link AbstractLdapRealm} if a
036 * factory is not explictly configured.</p>
037 *
038 * <p>Connection pooling is enabled by default on this factory, but can be disabled using the
039 * {@link #usePooling} property.</p>
040 *
041 * @author Jeremy Haile
042 * @since 0.2
043 */
044 public class DefaultLdapContextFactory implements LdapContextFactory {
045
046 //TODO - complete JavaDoc
047
048 /*--------------------------------------------
049 | C O N S T A N T S |
050 ============================================*/
051 /**
052 * The Sun LDAP property used to enable connection pooling. This is used in the default implementation
053 * to enable LDAP connection pooling.
054 */
055 protected static final String SUN_CONNECTION_POOLING_PROPERTY = "com.sun.jndi.ldap.connect.pool";
056
057 /*--------------------------------------------
058 | I N S T A N C E V A R I A B L E S |
059 ============================================*/
060
061 private static final Logger log = LoggerFactory.getLogger(DefaultLdapContextFactory.class);
062
063 protected String authentication = "simple";
064
065 protected String principalSuffix = null;
066
067 protected String searchBase = null;
068
069 protected String contextFactoryClassName = "com.sun.jndi.ldap.LdapCtxFactory";
070
071 protected String url = null;
072
073 protected String referral = "follow";
074
075 protected String systemUsername = null;
076
077 protected String systemPassword = null;
078
079 private boolean usePooling = true;
080
081 private Map<String, String> additionalEnvironment;
082
083 /*--------------------------------------------
084 | C O N S T R U C T O R S |
085 ============================================*/
086
087 /*--------------------------------------------
088 | A C C E S S O R S / M O D I F I E R S |
089 ============================================*/
090
091 /**
092 * Sets the type of LDAP authentication to perform when connecting to the LDAP server. Defaults to "simple"
093 *
094 * @param authentication the type of LDAP authentication to perform.
095 */
096 public void setAuthentication(String authentication) {
097 this.authentication = authentication;
098 }
099
100 /**
101 * A suffix appended to the username. This is typically for
102 * domain names. (e.g. "@MyDomain.local")
103 *
104 * @param principalSuffix the suffix.
105 */
106 public void setPrincipalSuffix(String principalSuffix) {
107 this.principalSuffix = principalSuffix;
108 }
109
110 /**
111 * The search base for the search to perform in the LDAP server.
112 * (e.g. OU=OrganizationName,DC=MyDomain,DC=local )
113 *
114 * @param searchBase the search base.
115 */
116 public void setSearchBase(String searchBase) {
117 this.searchBase = searchBase;
118 }
119
120 /**
121 * The context factory to use. This defaults to the SUN LDAP JNDI implementation
122 * but can be overridden to use custom LDAP factories.
123 *
124 * @param contextFactoryClassName the context factory that should be used.
125 */
126 public void setContextFactoryClassName(String contextFactoryClassName) {
127 this.contextFactoryClassName = contextFactoryClassName;
128 }
129
130 /**
131 * The LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>)
132 *
133 * @param url the LDAP url.
134 */
135 public void setUrl(String url) {
136 this.url = url;
137 }
138
139 /**
140 * Sets the LDAP referral property. Defaults to "follow"
141 *
142 * @param referral the referral property.
143 */
144 public void setReferral(String referral) {
145 this.referral = referral;
146 }
147
148 /**
149 * The system username that will be used when connecting to the LDAP server to retrieve authorization
150 * information about a user. This must be specified for LDAP authorization to work, but is not required for
151 * only authentication.
152 *
153 * @param systemUsername the username to use when logging into the LDAP server for authorization.
154 */
155 public void setSystemUsername(String systemUsername) {
156 this.systemUsername = systemUsername;
157 }
158
159
160 /**
161 * The system password that will be used when connecting to the LDAP server to retrieve authorization
162 * information about a user. This must be specified for LDAP authorization to work, but is not required for
163 * only authentication.
164 *
165 * @param systemPassword the password to use when logging into the LDAP server for authorization.
166 */
167 public void setSystemPassword(String systemPassword) {
168 this.systemPassword = systemPassword;
169 }
170
171 /**
172 * Determines whether or not LdapContext pooling is enabled for connections made using the system
173 * user account. In the default implementation, this simply
174 * sets the <tt>com.sun.jndi.ldap.connect.pool</tt> property in the LDAP context environment. If you use an
175 * LDAP Context Factory that is not Sun's default implementation, you will need to override the
176 * default behavior to use this setting in whatever way your underlying LDAP ContextFactory
177 * supports. By default, pooling is enabled.
178 *
179 * @param usePooling true to enable pooling, or false to disable it.
180 */
181 public void setUsePooling(boolean usePooling) {
182 this.usePooling = usePooling;
183 }
184
185 /**
186 * These entries are added to the environment map before initializing the LDAP context.
187 *
188 * @param additionalEnvironment additional environment entries to be configured on the LDAP context.
189 */
190 public void setAdditionalEnvironment(Map<String, String> additionalEnvironment) {
191 this.additionalEnvironment = additionalEnvironment;
192 }
193
194 /*--------------------------------------------
195 | M E T H O D S |
196 ============================================*/
197
198 public LdapContext getSystemLdapContext() throws NamingException {
199 return getLdapContext(systemUsername, systemPassword);
200 }
201
202 public LdapContext getLdapContext(String username, String password) throws NamingException {
203 if (searchBase == null) {
204 throw new IllegalStateException("A search base must be specified.");
205 }
206 if (url == null) {
207 throw new IllegalStateException("An LDAP URL must be specified of the form ldap://<hostname>:<port>");
208 }
209
210 if (username != null && principalSuffix != null) {
211 username += principalSuffix;
212 }
213
214 Hashtable<String, String> env = new Hashtable<String, String>();
215
216 env.put(Context.SECURITY_AUTHENTICATION, authentication);
217 if (username != null) {
218 env.put(Context.SECURITY_PRINCIPAL, username);
219 }
220 if (password != null) {
221 env.put(Context.SECURITY_CREDENTIALS, password);
222 }
223 env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactoryClassName);
224 env.put(Context.PROVIDER_URL, url);
225 env.put(Context.REFERRAL, referral);
226
227 // Only pool connections for system contexts
228 if (usePooling && username != null && username.equals(systemUsername)) {
229 // Enable connection pooling
230 env.put(SUN_CONNECTION_POOLING_PROPERTY, "true");
231 }
232
233 if (additionalEnvironment != null) {
234 env.putAll(additionalEnvironment);
235 }
236
237 if (log.isDebugEnabled()) {
238 log.debug("Initializing LDAP context using URL [" + url + "] and username [" + systemUsername + "] " +
239 "with pooling [" + (usePooling ? "enabled" : "disabled") + "]");
240 }
241
242 return new InitialLdapContext(env, null);
243 }
244 }