1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.security.spi.ldap;
18
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22
23 import javax.naming.NamingException;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.jetspeed.security.SecurityException;
28 import org.apache.jetspeed.security.spi.impl.ldap.LdapUserCredentialDao;
29
30 /***
31 * <p>
32 * Test the {@link LdapUserCredentialDao}.
33 * </p>
34 *
35 * @author <a href="mailto:mike.long@dataline.com">Mike Long </a>, <a href="mailto:dlestrat@apache.org">David Le Strat</a>
36 *
37 */
38 public class TestLdapUserCredentialDao extends AbstractLdapTest
39 {
40 /*** Configuration for the number of threads performing login. */
41 private static int NUMBER_OF_LOGIN_THREADS = 5;
42
43 /*** Configuration for the number of login per thread. */
44 private static int NUMBER_OF_LOGINS_PER_THREAD = 10;
45
46 /*** Map of login threads. */
47 private static Map loginThreads = new HashMap();
48
49 /*** The logger. */
50 private static final Log log = LogFactory.getLog(TestLdapUserCredentialDao.class);
51
52 /***
53 * @see org.apache.jetspeed.security.spi.ldap.AbstractLdapTest#setUp()
54 */
55 protected void setUp() throws Exception
56 {
57 super.setUp();
58 LdapDataHelper.seedUserData(uid1, password);
59 }
60
61 /***
62 * @see org.apache.jetspeed.security.spi.ldap.AbstractLdapTest#tearDown()
63 */
64 protected void tearDown() throws Exception
65 {
66 super.tearDown();
67 LdapDataHelper.removeUserData(uid1);
68 }
69
70 /***
71 * <p>
72 * Test <code>authenticate</code> with correct login.
73 * </p>
74 *
75 * @throws Exception An {@link Exception}.
76 */
77 public void testGoodLogin() throws Exception
78 {
79 assertTrue("The login failed for user.", ldapCredDao.authenticate(uid1, password));
80 }
81
82 /***
83 * <p>
84 * Test regular expression to match any of the following characters: ([{\^$|)?*+.
85 * </p>
86 *
87 * @throws Exception
88 */
89 public void testRegexForValidateUid() throws Exception
90 {
91 String pattern = ".*//(.*|.*//[.*|.*//{.*|.*////.*|.*//^.*|.*//$.*|.*//|.*|.*//).*|.*//?.*|.*//*.*|.*//+.*|.*//..*";
92 String s = "abcde";
93 assertFalse(s.matches(pattern));
94 s = "ba(cde";
95 assertTrue(s.matches(pattern));
96 s = "ba[cde";
97 assertTrue(s.matches(pattern));
98 s = "ba{cde";
99 assertTrue(s.matches(pattern));
100 s = "ba//cde";
101 assertTrue(s.matches(pattern));
102 s = "ba^cde";
103 assertTrue(s.matches(pattern));
104 s = "ba$cde";
105 assertTrue(s.matches(pattern));
106 s = "ba|cde";
107 assertTrue(s.matches(pattern));
108 s = "ba)cde";
109 assertTrue(s.matches(pattern));
110 s = "ba?cde";
111 assertTrue(s.matches(pattern));
112 s = "ba*cde";
113 assertTrue(s.matches(pattern));
114 s = "ba+cde";
115 assertTrue(s.matches(pattern));
116 s = "ba.cde";
117 assertTrue(s.matches(pattern));
118 }
119
120 /***
121 * <p>
122 * Test that the uid does not contain any of the following character:
123 * <code>([{\^$|)?*+.</code>
124 * </p>
125 *
126 * @throws Exception An {@link Exception}.
127 */
128 public void testRegularExpessionInUid() throws Exception
129 {
130
131 verifyRegularExpressionFails("(");
132 verifyRegularExpressionFails("[");
133 verifyRegularExpressionFails("{");
134 verifyRegularExpressionFails("//");
135 verifyRegularExpressionFails("^");
136 verifyRegularExpressionFails("$");
137 verifyRegularExpressionFails("|");
138 verifyRegularExpressionFails(")");
139 verifyRegularExpressionFails("?");
140 verifyRegularExpressionFails("*");
141 verifyRegularExpressionFails("+");
142 verifyRegularExpressionFails(".");
143 }
144
145 /***
146 * <p>
147 * Test <code>authenticate</code> with incorrect character in uid.
148 * </p>
149 *
150 * @throws Exception An {@link Exception}.
151 */
152 private void verifyRegularExpressionFails(String metaCharacter) throws Exception
153 {
154 try
155 {
156 ldapCredDao.authenticate(uid1 + metaCharacter, password);
157 fail("Should have thrown an SecurityException because the uid contained a regular expression meta-character.");
158 }
159 catch (Exception e)
160 {
161 assertTrue(
162 "Should have thrown an SecurityException because the uid contained a regular expression meta-character.",
163 e instanceof SecurityException);
164 }
165 }
166
167 /***
168 * <p>
169 * Test <code>authenticate</code> with no password.
170 * </p>
171 *
172 * @throws Exception An {@link Exception}.
173 */
174 public void testCannotAuthenticateWithNoPassword() throws Exception
175 {
176 try
177 {
178 ldapCredDao.authenticate(uid1, "");
179 fail("Should have thrown an SecurityException.");
180 }
181 catch (Exception e)
182 {
183 log.debug(e);
184 assertTrue("Should have thrown an SecurityException. Instead it threw:" + e.getClass().getName(),
185 e instanceof SecurityException);
186 }
187
188 try
189 {
190 ldapCredDao.authenticate(uid1, null);
191 fail("Should have thrown an SecurityException.");
192 }
193 catch (Exception e)
194 {
195 assertTrue("Should have thrown an SecurityException." + e, e instanceof SecurityException);
196 }
197 }
198
199 /***
200 * <p>
201 * Test <code>authenticate</code> with bad uid.
202 * </p>
203 *
204 * @throws Exception An {@link Exception}.
205 */
206 public void testBadUID() throws Exception
207 {
208
209 try
210 {
211 ldapCredDao.authenticate(uid1 + "123", password);
212 fail("Should have thrown an exception for a non-existant user.");
213 }
214 catch (Exception e)
215 {
216 assertTrue("Should have thrown a SecurityException for a non-existant user.",
217 e instanceof SecurityException);
218 }
219
220 }
221
222 /***
223 * <p>
224 * Test <code>authenticate</code> with bad password.
225 * </p>
226 *
227 * @throws Exception An {@link Exception}.
228 */
229 public void testBadPassword() throws Exception
230 {
231 assertFalse("Should not have authenticated with bad password.", ldapCredDao.authenticate(uid1, password + "123"));
232 }
233
234 /***
235 * <p>
236 * Test <code>authenticate</code> with concurrent logins.
237 * </p>
238 *
239 * @throws Exception An {@link Exception}.
240 */
241 public void testConcurrentLogins() throws Exception
242 {
243 for (int i = 0; i < NUMBER_OF_LOGIN_THREADS; i++)
244 {
245 LoginThread thread = new LoginThread();
246
247 thread.start();
248 }
249
250 Thread.sleep(6000);
251 assertTrue("Not all login threads completed.", loginThreads.size() == NUMBER_OF_LOGIN_THREADS);
252 assertTrue("Not all login threads successfully ran all their logins().", allLoginThreadsCompletedTheirLogins());
253 assertFalse("An exception was thrown by a login thread. This means there is a concurrency problem.",
254 exceptionThrownByLogin());
255 }
256
257 /***
258 * <p>
259 * Gets the exception thrown by the login operation.
260 * </p>
261 */
262 private boolean exceptionThrownByLogin()
263 {
264 boolean exceptionThrown = false;
265 Iterator loginThreadStatuses = loginThreads.values().iterator();
266
267 while (loginThreadStatuses.hasNext())
268 {
269 LoginThreadStatus status = (LoginThreadStatus) loginThreadStatuses.next();
270
271 if (status.isSomeExceptionThrown())
272 {
273 exceptionThrown = true;
274 }
275 }
276
277 return exceptionThrown;
278 }
279
280 /***
281 * <p>
282 * Whether all login thread completed their login.
283 * </p>
284 */
285 private boolean allLoginThreadsCompletedTheirLogins()
286 {
287 boolean allThreadsCompletedTheirLogins = true;
288 Iterator loginThreadStatuses = loginThreads.values().iterator();
289
290 while (loginThreadStatuses.hasNext())
291 {
292 LoginThreadStatus status = (LoginThreadStatus) loginThreadStatuses.next();
293
294 if (status.getNumberOfSuccessfulLogins() < NUMBER_OF_LOGINS_PER_THREAD)
295 {
296 allThreadsCompletedTheirLogins = false;
297 }
298 }
299
300 return allThreadsCompletedTheirLogins;
301 }
302
303 /***
304 * <p>
305 * Login threads.
306 * </p>
307 */
308 private class LoginThread extends Thread
309 {
310 /*** The login thread status. */
311 private LoginThreadStatus status = new LoginThreadStatus();
312
313 /*** The {@link LdapUserCredentialDao}. */
314 private LdapUserCredentialDao threadLdap;
315
316 public LoginThread() throws NamingException, SecurityException
317 {
318 threadLdap = ldapCredDao;
319 }
320
321 /***
322 * @see java.lang.Runnable#run()
323 */
324 public void run()
325 {
326 for (int i = 0; i < NUMBER_OF_LOGINS_PER_THREAD; i++)
327 {
328 try
329 {
330 assertTrue("The login failed for user.", threadLdap.authenticate(uid1, password));
331 status.incrementNumberOfSuccessfulLogins();
332 }
333 catch (Exception e)
334 {
335 status.setSomeExceptionThrown(true);
336 }
337 }
338
339 TestLdapUserCredentialDao.loginThreads.put(this, status);
340 }
341 }
342 }
343
344 /***
345 * <p>
346 * The Login thread status.
347 * </p>
348 */
349
350 class LoginThreadStatus
351 {
352 private int numberOfSuccessfulLogins;
353
354 private boolean someExceptionThrown;
355
356 void incrementNumberOfSuccessfulLogins()
357 {
358 this.numberOfSuccessfulLogins++;
359 }
360
361 int getNumberOfSuccessfulLogins()
362 {
363 return numberOfSuccessfulLogins;
364 }
365
366 void setSomeExceptionThrown(boolean someExceptionThrown)
367 {
368 this.someExceptionThrown = someExceptionThrown;
369 }
370
371 boolean isSomeExceptionThrown()
372 {
373 return someExceptionThrown;
374 }
375 }