1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.ldap;
17
18 import org.springframework.dao.DataAccessException;
19 import org.springframework.dao.IncorrectResultSizeDataAccessException;
20
21 import org.springframework.util.Assert;
22 import org.springframework.util.StringUtils;
23
24 import java.util.HashSet;
25 import java.util.Set;
26
27 import javax.naming.NameNotFoundException;
28 import javax.naming.NamingEnumeration;
29 import javax.naming.NamingException;
30 import javax.naming.Context;
31 import javax.naming.directory.Attribute;
32 import javax.naming.directory.Attributes;
33 import javax.naming.directory.DirContext;
34 import javax.naming.directory.SearchControls;
35 import javax.naming.directory.SearchResult;
36
37
38
39
40
41
42
43
44
45 public class LdapTemplate {
46
47
48 public static final String[] NO_ATTRS = new String[0];
49
50
51
52 private InitialDirContextFactory dirContextFactory;
53 private NamingExceptionTranslator exceptionTranslator = new LdapExceptionTranslator();
54
55
56 private SearchControls searchControls = new SearchControls();
57 private String password = null;
58 private String principalDn = null;
59
60
61
62 public LdapTemplate(InitialDirContextFactory dirContextFactory) {
63 Assert.notNull(dirContextFactory, "An InitialDirContextFactory is required");
64 this.dirContextFactory = dirContextFactory;
65
66 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
67 }
68
69
70
71
72
73
74
75 public LdapTemplate(InitialDirContextFactory dirContextFactory, String userDn, String password) {
76 this(dirContextFactory);
77
78 Assert.hasLength(userDn, "userDn must not be null or empty");
79 Assert.notNull(password, "password cannot be null");
80
81 this.principalDn = userDn;
82 this.password = password;
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96 public boolean compare(final String dn, final String attributeName, final Object value) {
97 final String comparisonFilter = "(" + attributeName + "={0})";
98
99 class LdapCompareCallback implements LdapCallback {
100 public Object doInDirContext(DirContext ctx)
101 throws NamingException {
102 SearchControls ctls = new SearchControls();
103 ctls.setReturningAttributes(NO_ATTRS);
104 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
105
106 String relativeName = LdapUtils.getRelativeName(dn, ctx);
107
108 NamingEnumeration results = ctx.search(relativeName, comparisonFilter, new Object[] {value}, ctls);
109
110 return Boolean.valueOf(results.hasMore());
111 }
112 }
113
114 Boolean matches = (Boolean) execute(new LdapCompareCallback());
115
116 return matches.booleanValue();
117 }
118
119 public Object execute(LdapCallback callback) throws DataAccessException {
120 DirContext ctx = null;
121
122 try {
123 ctx = (principalDn == null) ? dirContextFactory.newInitialDirContext()
124 : dirContextFactory.newInitialDirContext(principalDn, password);
125
126 return callback.doInDirContext(ctx);
127 } catch (NamingException exception) {
128 throw exceptionTranslator.translate("LdapCallback", exception);
129 } finally {
130 LdapUtils.closeContext(ctx);
131 }
132 }
133
134 public boolean nameExists(final String dn) {
135 Boolean exists = (Boolean) execute(new LdapCallback() {
136 public Object doInDirContext(DirContext ctx)
137 throws NamingException {
138 try {
139 Object obj = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
140 if (obj instanceof Context) {
141 LdapUtils.closeContext((Context) obj);
142 }
143
144 } catch (NameNotFoundException nnfe) {
145 return Boolean.FALSE;
146 }
147
148 return Boolean.TRUE;
149 }
150 });
151
152 return exists.booleanValue();
153 }
154
155
156
157
158
159
160
161
162
163
164 public Object retrieveEntry(final String dn, final LdapEntryMapper mapper, final String[] attributesToRetrieve) {
165 return execute(new LdapCallback() {
166 public Object doInDirContext(DirContext ctx)
167 throws NamingException {
168 return mapper.mapAttributes(dn,
169 ctx.getAttributes(LdapUtils.getRelativeName(dn, ctx), attributesToRetrieve));
170 }
171 });
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186 public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
187 final String attributeName) {
188 class SingleAttributeSearchCallback implements LdapCallback {
189 public Object doInDirContext(DirContext ctx)
190 throws NamingException {
191 Set unionOfValues = new HashSet();
192
193
194
195 SearchControls ctls = new SearchControls();
196
197 ctls.setSearchScope(searchControls.getSearchScope());
198 ctls.setTimeLimit(searchControls.getTimeLimit());
199 ctls.setDerefLinkFlag(searchControls.getDerefLinkFlag());
200 ctls.setReturningAttributes(new String[] {attributeName});
201
202 NamingEnumeration matchingEntries = ctx.search(base, filter, params, ctls);
203
204 while (matchingEntries.hasMore()) {
205 SearchResult result = (SearchResult) matchingEntries.next();
206 Attributes attrs = result.getAttributes();
207
208
209 NamingEnumeration returnedAttributes = attrs.getAll();
210
211 while (returnedAttributes.hasMore()) {
212 Attribute returnedAttribute = (Attribute) returnedAttributes.next();
213 NamingEnumeration attributeValues = returnedAttribute.getAll();
214
215 while (attributeValues.hasMore()) {
216 Object value = attributeValues.next();
217
218 unionOfValues.add(value.toString());
219 }
220 }
221 }
222
223 return unionOfValues;
224 }
225 }
226
227 return (Set) execute(new SingleAttributeSearchCallback());
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 public Object searchForSingleEntry(final String base, final String filter, final Object[] params,
245 final LdapEntryMapper mapper) {
246 return execute(new LdapCallback() {
247 public Object doInDirContext(DirContext ctx)
248 throws NamingException {
249 NamingEnumeration results = ctx.search(base, filter, params, searchControls);
250
251 if (!results.hasMore()) {
252 throw new IncorrectResultSizeDataAccessException(1, 0);
253 }
254
255 SearchResult searchResult = (SearchResult) results.next();
256
257 if (results.hasMore()) {
258
259 throw new IncorrectResultSizeDataAccessException(1, 2);
260 }
261
262
263 StringBuffer dn = new StringBuffer(searchResult.getName());
264
265 if (base.length() > 0) {
266 dn.append(",");
267 dn.append(base);
268 }
269
270 String nameInNamespace = ctx.getNameInNamespace();
271
272 if (StringUtils.hasLength(nameInNamespace)) {
273 dn.append(",");
274 dn.append(nameInNamespace);
275 }
276
277 return mapper.mapAttributes(dn.toString(), searchResult.getAttributes());
278 }
279 });
280 }
281
282
283
284
285
286
287 public void setSearchControls(SearchControls searchControls) {
288 this.searchControls = searchControls;
289 }
290
291
292
293 private static class LdapExceptionTranslator implements NamingExceptionTranslator {
294 public DataAccessException translate(String task, NamingException e) {
295 return new LdapDataAccessException(task + ";" + e.getMessage(), e);
296 }
297 }
298 }