001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.security.spi.ldap;
018:
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import javax.naming.NamingException;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.jetspeed.security.SecurityException;
028: import org.apache.jetspeed.security.spi.impl.ldap.LdapUserCredentialDao;
029:
030: /**
031: * <p>
032: * Test the {@link LdapUserCredentialDao}.
033: * </p>
034: *
035: * @author <a href="mailto:mike.long@dataline.com">Mike Long </a>, <a href="mailto:dlestrat@apache.org">David Le Strat</a>
036: *
037: */
038: public class TestLdapUserCredentialDao extends AbstractLdapTest {
039: /** Configuration for the number of threads performing login. */
040: private static int NUMBER_OF_LOGIN_THREADS = 5;
041:
042: /** Configuration for the number of login per thread. */
043: private static int NUMBER_OF_LOGINS_PER_THREAD = 10;
044:
045: /** Map of login threads. */
046: private static Map loginThreads = new HashMap();
047:
048: /** The logger. */
049: private static final Log log = LogFactory
050: .getLog(TestLdapUserCredentialDao.class);
051:
052: /**
053: * @see org.apache.jetspeed.security.spi.ldap.AbstractLdapTest#setUp()
054: */
055: protected void setUp() throws Exception {
056: super .setUp();
057: LdapDataHelper.seedUserData(uid1, password);
058: }
059:
060: /**
061: * @see org.apache.jetspeed.security.spi.ldap.AbstractLdapTest#tearDown()
062: */
063: protected void tearDown() throws Exception {
064: super .tearDown();
065: LdapDataHelper.removeUserData(uid1);
066: }
067:
068: /**
069: * <p>
070: * Test <code>authenticate</code> with correct login.
071: * </p>
072: *
073: * @throws Exception An {@link Exception}.
074: */
075: public void testGoodLogin() throws Exception {
076: assertTrue("The login failed for user.", ldapCredDao
077: .authenticate(uid1, password));
078: }
079:
080: /**
081: * <p>
082: * Test regular expression to match any of the following characters: ([{\^$|)?*+.
083: * </p>
084: *
085: * @throws Exception
086: */
087: public void testRegexForValidateUid() throws Exception {
088: String pattern = ".*\\(.*|.*\\[.*|.*\\{.*|.*\\\\.*|.*\\^.*|.*\\$.*|.*\\|.*|.*\\).*|.*\\?.*|.*\\*.*|.*\\+.*|.*\\..*";
089: String s = "abcde";
090: assertFalse(s.matches(pattern));
091: s = "ba(cde";
092: assertTrue(s.matches(pattern));
093: s = "ba[cde";
094: assertTrue(s.matches(pattern));
095: s = "ba{cde";
096: assertTrue(s.matches(pattern));
097: s = "ba\\cde";
098: assertTrue(s.matches(pattern));
099: s = "ba^cde";
100: assertTrue(s.matches(pattern));
101: s = "ba$cde";
102: assertTrue(s.matches(pattern));
103: s = "ba|cde";
104: assertTrue(s.matches(pattern));
105: s = "ba)cde";
106: assertTrue(s.matches(pattern));
107: s = "ba?cde";
108: assertTrue(s.matches(pattern));
109: s = "ba*cde";
110: assertTrue(s.matches(pattern));
111: s = "ba+cde";
112: assertTrue(s.matches(pattern));
113: s = "ba.cde";
114: assertTrue(s.matches(pattern));
115: }
116:
117: /**
118: * <p>
119: * Test that the uid does not contain any of the following character:
120: * <code>([{\^$|)?*+.</code>
121: * </p>
122: *
123: * @throws Exception An {@link Exception}.
124: */
125: public void testRegularExpessionInUid() throws Exception {
126: // ([{\^$|)?*+.
127: verifyRegularExpressionFails("(");
128: verifyRegularExpressionFails("[");
129: verifyRegularExpressionFails("{");
130: verifyRegularExpressionFails("\\");
131: verifyRegularExpressionFails("^");
132: verifyRegularExpressionFails("$");
133: verifyRegularExpressionFails("|");
134: verifyRegularExpressionFails(")");
135: verifyRegularExpressionFails("?");
136: verifyRegularExpressionFails("*");
137: verifyRegularExpressionFails("+");
138: verifyRegularExpressionFails(".");
139: }
140:
141: /**
142: * <p>
143: * Test <code>authenticate</code> with incorrect character in uid.
144: * </p>
145: *
146: * @throws Exception An {@link Exception}.
147: */
148: private void verifyRegularExpressionFails(String metaCharacter)
149: throws Exception {
150: try {
151: ldapCredDao.authenticate(uid1 + metaCharacter, password);
152: fail("Should have thrown an SecurityException because the uid contained a regular expression meta-character.");
153: } catch (Exception e) {
154: assertTrue(
155: "Should have thrown an SecurityException because the uid contained a regular expression meta-character.",
156: e instanceof SecurityException);
157: }
158: }
159:
160: /**
161: * <p>
162: * Test <code>authenticate</code> with no password.
163: * </p>
164: *
165: * @throws Exception An {@link Exception}.
166: */
167: public void testCannotAuthenticateWithNoPassword() throws Exception {
168: try {
169: ldapCredDao.authenticate(uid1, "");
170: fail("Should have thrown an SecurityException.");
171: } catch (Exception e) {
172: log.debug(e);
173: assertTrue(
174: "Should have thrown an SecurityException. Instead it threw:"
175: + e.getClass().getName(),
176: e instanceof SecurityException);
177: }
178:
179: try {
180: ldapCredDao.authenticate(uid1, null);
181: fail("Should have thrown an SecurityException.");
182: } catch (Exception e) {
183: assertTrue("Should have thrown an SecurityException." + e,
184: e instanceof SecurityException);
185: }
186: }
187:
188: /**
189: * <p>
190: * Test <code>authenticate</code> with bad uid.
191: * </p>
192: *
193: * @throws Exception An {@link Exception}.
194: */
195: public void testBadUID() throws Exception {
196:
197: try {
198: ldapCredDao.authenticate(uid1 + "123", password);
199: fail("Should have thrown an exception for a non-existant user.");
200: } catch (Exception e) {
201: assertTrue(
202: "Should have thrown a SecurityException for a non-existant user.",
203: e instanceof SecurityException);
204: }
205:
206: }
207:
208: /**
209: * <p>
210: * Test <code>authenticate</code> with bad password.
211: * </p>
212: *
213: * @throws Exception An {@link Exception}.
214: */
215: public void testBadPassword() throws Exception {
216: assertFalse("Should not have authenticated with bad password.",
217: ldapCredDao.authenticate(uid1, password + "123"));
218: }
219:
220: /**
221: * <p>
222: * Test <code>authenticate</code> with concurrent logins.
223: * </p>
224: *
225: * @throws Exception An {@link Exception}.
226: */
227: public void testConcurrentLogins() throws Exception {
228: for (int i = 0; i < NUMBER_OF_LOGIN_THREADS; i++) {
229: LoginThread thread = new LoginThread();
230:
231: thread.start();
232: }
233:
234: Thread.sleep(6000);
235: assertTrue("Not all login threads completed.", loginThreads
236: .size() == NUMBER_OF_LOGIN_THREADS);
237: assertTrue(
238: "Not all login threads successfully ran all their logins().",
239: allLoginThreadsCompletedTheirLogins());
240: assertFalse(
241: "An exception was thrown by a login thread. This means there is a concurrency problem.",
242: exceptionThrownByLogin());
243: }
244:
245: /**
246: * <p>
247: * Gets the exception thrown by the login operation.
248: * </p>
249: */
250: private boolean exceptionThrownByLogin() {
251: boolean exceptionThrown = false;
252: Iterator loginThreadStatuses = loginThreads.values().iterator();
253:
254: while (loginThreadStatuses.hasNext()) {
255: LoginThreadStatus status = (LoginThreadStatus) loginThreadStatuses
256: .next();
257:
258: if (status.isSomeExceptionThrown()) {
259: exceptionThrown = true;
260: }
261: }
262:
263: return exceptionThrown;
264: }
265:
266: /**
267: * <p>
268: * Whether all login thread completed their login.
269: * </p>
270: */
271: private boolean allLoginThreadsCompletedTheirLogins() {
272: boolean allThreadsCompletedTheirLogins = true;
273: Iterator loginThreadStatuses = loginThreads.values().iterator();
274:
275: while (loginThreadStatuses.hasNext()) {
276: LoginThreadStatus status = (LoginThreadStatus) loginThreadStatuses
277: .next();
278:
279: if (status.getNumberOfSuccessfulLogins() < NUMBER_OF_LOGINS_PER_THREAD) {
280: allThreadsCompletedTheirLogins = false;
281: }
282: }
283:
284: return allThreadsCompletedTheirLogins;
285: }
286:
287: /**
288: * <p>
289: * Login threads.
290: * </p>
291: */
292: private class LoginThread extends Thread {
293: /** The login thread status. */
294: private LoginThreadStatus status = new LoginThreadStatus();
295:
296: /** The {@link LdapUserCredentialDao}. */
297: private LdapUserCredentialDao threadLdap;
298:
299: public LoginThread() throws NamingException, SecurityException {
300: threadLdap = ldapCredDao;
301: }
302:
303: /**
304: * @see java.lang.Runnable#run()
305: */
306: public void run() {
307: for (int i = 0; i < NUMBER_OF_LOGINS_PER_THREAD; i++) {
308: try {
309: assertTrue("The login failed for user.", threadLdap
310: .authenticate(uid1, password));
311: status.incrementNumberOfSuccessfulLogins();
312: } catch (Exception e) {
313: status.setSomeExceptionThrown(true);
314: }
315: }
316:
317: TestLdapUserCredentialDao.loginThreads.put(this , status);
318: }
319: }
320: }
321:
322: /**
323: * <p>
324: * The Login thread status.
325: * </p>
326: */
327:
328: class LoginThreadStatus {
329: private int numberOfSuccessfulLogins;
330:
331: private boolean someExceptionThrown;
332:
333: void incrementNumberOfSuccessfulLogins() {
334: this .numberOfSuccessfulLogins++;
335: }
336:
337: int getNumberOfSuccessfulLogins() {
338: return numberOfSuccessfulLogins;
339: }
340:
341: void setSomeExceptionThrown(boolean someExceptionThrown) {
342: this .someExceptionThrown = someExceptionThrown;
343: }
344:
345: boolean isSomeExceptionThrown() {
346: return someExceptionThrown;
347: }
348: }
|