1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.security.ldap;
17
18 import org.springframework.security.SpringSecurityMessageSource;
19 import org.springframework.security.BadCredentialsException;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23
24 import org.springframework.context.MessageSource;
25 import org.springframework.context.MessageSourceAware;
26 import org.springframework.context.support.MessageSourceAccessor;
27
28 import org.springframework.util.Assert;
29 import org.springframework.ldap.UncategorizedLdapException;
30 import org.springframework.ldap.core.support.DefaultDirObjectFactory;
31 import org.springframework.ldap.core.DistinguishedName;
32 import org.springframework.dao.DataAccessException;
33
34 import java.util.Hashtable;
35 import java.util.Map;
36 import java.util.StringTokenizer;
37
38 import javax.naming.CommunicationException;
39 import javax.naming.Context;
40 import javax.naming.NamingException;
41 import javax.naming.OperationNotSupportedException;
42 import javax.naming.ldap.InitialLdapContext;
43 import javax.naming.directory.DirContext;
44 import javax.naming.directory.InitialDirContext;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public class DefaultInitialDirContextFactory implements InitialDirContextFactory,
75 SpringSecurityContextSource, MessageSourceAware {
76
77
78 private static final Log logger = LogFactory.getLog(DefaultInitialDirContextFactory.class);
79 private static final String CONNECTION_POOL_KEY = "com.sun.jndi.ldap.connect.pool";
80 private static final String AUTH_TYPE_NONE = "none";
81
82
83
84
85 private Map extraEnvVars = null;
86 protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
87
88
89 private String authenticationType = "simple";
90
91
92
93
94
95 private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
96
97 private String dirObjectFactoryClass = DefaultDirObjectFactory.class.getName();
98
99
100
101
102
103 private String managerDn = null;
104
105
106 private String managerPassword = "manager_password_not_set";
107
108
109 private String providerUrl;
110
111
112
113
114
115 private String rootDn = null;
116
117
118
119
120
121 private boolean useConnectionPool = true;
122
123
124 private boolean useLdapContext = false;
125
126
127
128
129
130
131
132
133 public DefaultInitialDirContextFactory(String providerUrl) {
134 this.setProviderUrl(providerUrl);
135 }
136
137
138
139
140
141
142
143
144 private void setProviderUrl(String providerUrl) {
145 Assert.hasLength(providerUrl, "An LDAP connection URL must be supplied.");
146
147 this.providerUrl = providerUrl;
148
149 StringTokenizer st = new StringTokenizer(providerUrl);
150
151
152 while (st.hasMoreTokens()) {
153 String url = st.nextToken();
154 String urlRootDn = LdapUtils.parseRootDnFromUrl(url);
155
156 logger.info(" URL '" + url + "', root DN is '" + urlRootDn + "'");
157
158 if (rootDn == null) {
159 rootDn = urlRootDn;
160 } else if (!rootDn.equals(urlRootDn)) {
161 throw new IllegalArgumentException("Root DNs must be the same when using multiple URLs");
162 }
163 }
164
165
166
167 }
168
169
170
171
172
173
174 private String getProviderUrl() {
175 return providerUrl;
176 }
177
178 private InitialDirContext connect(Hashtable env) {
179 if (logger.isDebugEnabled()) {
180 Hashtable envClone = (Hashtable) env.clone();
181
182 if (envClone.containsKey(Context.SECURITY_CREDENTIALS)) {
183 envClone.put(Context.SECURITY_CREDENTIALS, "******");
184 }
185
186 logger.debug("Creating InitialDirContext with environment " + envClone);
187 }
188
189 try {
190 return useLdapContext ? new InitialLdapContext(env, null) : new InitialDirContext(env);
191 } catch (NamingException ne) {
192 if ((ne instanceof javax.naming.AuthenticationException)
193 || (ne instanceof OperationNotSupportedException)) {
194 throw new BadCredentialsException(messages.getMessage("DefaultIntitalDirContextFactory.badCredentials",
195 "Bad credentials"), ne);
196 }
197
198 if (ne instanceof CommunicationException) {
199 throw new UncategorizedLdapException(messages.getMessage(
200 "DefaultIntitalDirContextFactory.communicationFailure", "Unable to connect to LDAP server"), ne);
201 }
202
203 throw new UncategorizedLdapException(messages.getMessage(
204 "DefaultIntitalDirContextFactory.unexpectedException",
205 "Failed to obtain InitialDirContext due to unexpected exception"), ne);
206 }
207 }
208
209
210
211
212
213
214 protected Hashtable getEnvironment() {
215 Hashtable env = new Hashtable();
216
217 env.put(Context.SECURITY_AUTHENTICATION, authenticationType);
218 env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
219 env.put(Context.PROVIDER_URL, getProviderUrl());
220
221 if (useConnectionPool) {
222 env.put(CONNECTION_POOL_KEY, "true");
223 }
224
225 if ((extraEnvVars != null) && (extraEnvVars.size() > 0)) {
226 env.putAll(extraEnvVars);
227 }
228
229 return env;
230 }
231
232
233
234
235
236
237
238
239 public String getRootDn() {
240 return rootDn;
241 }
242
243
244
245
246
247
248
249 public DirContext newInitialDirContext() {
250 if (managerDn != null) {
251 return newInitialDirContext(managerDn, managerPassword);
252 }
253
254 Hashtable env = getEnvironment();
255 env.put(Context.SECURITY_AUTHENTICATION, AUTH_TYPE_NONE);
256
257 return connect(env);
258 }
259
260 public DirContext newInitialDirContext(String username, String password) {
261 Hashtable env = getEnvironment();
262
263
264 if (!username.equals(managerDn)) {
265 env.remove(CONNECTION_POOL_KEY);
266 }
267
268 env.put(Context.SECURITY_PRINCIPAL, username);
269 env.put(Context.SECURITY_CREDENTIALS, password);
270
271 if(dirObjectFactoryClass != null) {
272 env.put(Context.OBJECT_FACTORIES, dirObjectFactoryClass);
273 }
274
275 return connect(env);
276 }
277
278
279 public DirContext getReadOnlyContext() throws DataAccessException {
280 return newInitialDirContext();
281 }
282
283
284 public DirContext getReadWriteContext() throws DataAccessException {
285 return newInitialDirContext();
286 }
287
288 public void setAuthenticationType(String authenticationType) {
289 Assert.hasLength(authenticationType, "LDAP Authentication type must not be empty or null");
290 this.authenticationType = authenticationType;
291 }
292
293
294
295
296
297
298
299 public void setExtraEnvVars(Map extraEnvVars) {
300 Assert.notNull(extraEnvVars, "Extra environment map cannot be null.");
301 this.extraEnvVars = extraEnvVars;
302 }
303
304 public void setInitialContextFactory(String initialContextFactory) {
305 Assert.hasLength(initialContextFactory, "Initial context factory name cannot be empty or null");
306 this.initialContextFactory = initialContextFactory;
307 }
308
309
310
311
312
313
314
315
316 public void setManagerDn(String managerDn) {
317 Assert.hasLength(managerDn, "Manager user name cannot be empty or null.");
318 this.managerDn = managerDn;
319 }
320
321
322
323
324
325
326 public void setManagerPassword(String managerPassword) {
327 Assert.hasLength(managerPassword, "Manager password must not be empty or null.");
328 this.managerPassword = managerPassword;
329 }
330
331 public void setMessageSource(MessageSource messageSource) {
332 this.messages = new MessageSourceAccessor(messageSource);
333 }
334
335
336
337
338
339
340
341 public void setUseConnectionPool(boolean useConnectionPool) {
342 this.useConnectionPool = useConnectionPool;
343 }
344
345 public void setUseLdapContext(boolean useLdapContext) {
346 this.useLdapContext = useLdapContext;
347 }
348
349 public void setDirObjectFactory(String dirObjectFactory) {
350 this.dirObjectFactoryClass = dirObjectFactory;
351 }
352
353 public DirContext getReadWriteContext(String userDn, Object credentials) {
354 return newInitialDirContext(userDn, (String) credentials);
355 }
356
357 public DistinguishedName getBaseLdapPath() {
358 return new DistinguishedName(rootDn);
359 }
360
361 public String getBaseLdapPathAsString() {
362 return getBaseLdapPath().toString();
363 }
364 }