1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.security.ldap;
17
18 import java.text.MessageFormat;
19 import java.util.Arrays;
20 import java.util.HashSet;
21 import java.util.Set;
22
23 import javax.naming.NamingEnumeration;
24 import javax.naming.NamingException;
25 import javax.naming.PartialResultException;
26 import javax.naming.directory.Attributes;
27 import javax.naming.directory.DirContext;
28 import javax.naming.directory.SearchControls;
29 import javax.naming.directory.SearchResult;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.springframework.dao.IncorrectResultSizeDataAccessException;
34 import org.springframework.ldap.core.ContextExecutor;
35 import org.springframework.ldap.core.ContextMapper;
36 import org.springframework.ldap.core.ContextSource;
37 import org.springframework.ldap.core.DirContextAdapter;
38 import org.springframework.ldap.core.DirContextOperations;
39 import org.springframework.ldap.core.DistinguishedName;
40 import org.springframework.ldap.core.LdapEncoder;
41 import org.springframework.ldap.core.LdapTemplate;
42 import org.springframework.util.Assert;
43
44
45
46
47
48
49
50
51
52 public class SpringSecurityLdapTemplate extends LdapTemplate {
53
54 private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);
55
56 public static final String[] NO_ATTRS = new String[0];
57
58
59
60
61 private SearchControls searchControls = new SearchControls();
62
63
64
65 public SpringSecurityLdapTemplate(ContextSource contextSource) {
66 Assert.notNull(contextSource, "ContextSource cannot be null");
67 setContextSource(contextSource);
68
69 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83 public boolean compare(final String dn, final String attributeName, final Object value) {
84 final String comparisonFilter = "(" + attributeName + "={0})";
85
86 class LdapCompareCallback implements ContextExecutor {
87
88 public Object executeWithContext(DirContext ctx) throws NamingException {
89 SearchControls ctls = new SearchControls();
90 ctls.setReturningAttributes(NO_ATTRS);
91 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
92
93 NamingEnumeration results = ctx.search(dn, comparisonFilter, new Object[] {value}, ctls);
94
95 return Boolean.valueOf(results.hasMore());
96 }
97 }
98
99 Boolean matches = (Boolean) executeReadOnly(new LdapCompareCallback());
100
101 return matches.booleanValue();
102 }
103
104
105
106
107
108
109
110
111
112 public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
113
114 return (DirContextOperations) executeReadOnly(new ContextExecutor() {
115 public Object executeWithContext(DirContext ctx) throws NamingException {
116 Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
117
118
119
120 return new DirContextAdapter(attrs, new DistinguishedName(dn),
121 new DistinguishedName(ctx.getNameInNamespace()));
122 }
123 });
124 }
125
126
127
128
129
130
131
132
133
134
135
136
137
138 public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
139 final String attributeName) {
140
141 Object[] encodedParams = new String[params.length];
142
143 for (int i=0; i < params.length; i++) {
144 encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
145 }
146
147 String formattedFilter = MessageFormat.format(filter, encodedParams);
148 logger.debug("Using filter: " + formattedFilter);
149
150 final HashSet set = new HashSet();
151
152 ContextMapper roleMapper = new ContextMapper() {
153 public Object mapFromContext(Object ctx) {
154 DirContextAdapter adapter = (DirContextAdapter) ctx;
155 String[] values = adapter.getStringAttributes(attributeName);
156 if (values == null || values.length == 0) {
157 logger.debug("No attribute value found for '" + attributeName + "'");
158 } else {
159 set.addAll(Arrays.asList(values));
160 }
161 return null;
162 }
163 };
164
165 SearchControls ctls = new SearchControls();
166 ctls.setSearchScope(searchControls.getSearchScope());
167 ctls.setReturningAttributes(new String[] {attributeName});
168 ctls.setReturningObjFlag(false);
169
170 search(base, formattedFilter, ctls, roleMapper);
171
172 return set;
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) {
192
193 return (DirContextOperations) executeReadOnly(new ContextExecutor() {
194 public Object executeWithContext(DirContext ctx) throws NamingException {
195 DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
196 NamingEnumeration resultsEnum = ctx.search(base, filter, params, searchControls);
197 Set results = new HashSet();
198 try {
199 while (resultsEnum.hasMore()) {
200
201 SearchResult searchResult = (SearchResult) resultsEnum.next();
202
203 StringBuffer dn = new StringBuffer(searchResult.getName());
204
205 if (base.length() > 0) {
206 dn.append(",");
207 dn.append(base);
208 }
209
210 results.add(new DirContextAdapter(searchResult.getAttributes(),
211 new DistinguishedName(dn.toString()), ctxBaseDn));
212 }
213 } catch (PartialResultException e) {
214 logger.info("Ignoring PartialResultException");
215 }
216
217 if (results.size() == 0) {
218 throw new IncorrectResultSizeDataAccessException(1, 0);
219 }
220
221 if (results.size() > 1) {
222 throw new IncorrectResultSizeDataAccessException(1, results.size());
223 }
224
225 return results.toArray()[0];
226 }
227 });
228 }
229
230
231
232
233
234
235 public void setSearchControls(SearchControls searchControls) {
236 this.searchControls = searchControls;
237 }
238 }