281684b4c01db15d66b807e74fa210855b08662d
[utils] / security / impl / src / test / java / org / wamblee / security / authentication / UserAdministrationImplTest.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */ 
16 package org.wamblee.security.authentication;
17
18 import junit.framework.TestCase;
19
20 import org.apache.log4j.Logger;
21
22 import org.wamblee.security.authentication.Group;
23 import org.wamblee.security.authentication.GroupSet;
24 import org.wamblee.security.authentication.InMemoryGroupSet;
25 import org.wamblee.security.authentication.InMemoryUserSet;
26 import org.wamblee.security.authentication.RegexpNameValidator;
27 import org.wamblee.security.authentication.User;
28 import org.wamblee.security.authentication.UserAdministration;
29 import org.wamblee.security.authentication.UserAdministrationImpl;
30 import org.wamblee.security.authentication.UserMgtException;
31 import org.wamblee.security.authentication.UserSet;
32 import org.wamblee.security.authentication.UserMgtException.Reason;
33 import org.wamblee.security.encryption.Md5HexMessageDigester;
34
35
36 import java.util.Set;
37
38 /**
39  * Test of user administration implementation.
40  * 
41  * @author Erik Brakkee
42  */
43 public class UserAdministrationImplTest extends TestCase {
44     private static final Logger LOGGER = Logger
45         .getLogger(UserAdministrationImplTest.class);
46
47     private static final String USER1 = "piet";
48
49     private static final String PASS1 = "passpiet";
50
51     private static final String USER2 = "kees";
52
53     private static final String PASS2 = "passkees";
54
55     private static final String GROUP1 = "cyclists";
56
57     private static final String GROUP2 = "runners";
58
59     private UserAdministration admin;
60
61     /*
62      * (non-Javadoc)
63      * 
64      * @see junit.framework.TestCase#setUp()
65      */
66     @Override
67     protected void setUp() throws Exception {
68         super.setUp();
69         admin = createAdmin();
70     }
71
72     protected UserAdministration createAdmin() {
73         UserSet users = new InMemoryUserSet(new RegexpNameValidator(
74             RegexpNameValidator.PASSWORD_PATTERN, Reason.INVALID_PASSWORD,
75             "Password must contain at least 6 characters"),
76             new Md5HexMessageDigester());
77         GroupSet groups = new InMemoryGroupSet();
78
79         return new UserAdministrationImpl(users, groups,
80             new RegexpNameValidator(RegexpNameValidator.ID_PATTERN,
81                 Reason.INVALID_USERNAME, "Invalid user"),
82             new RegexpNameValidator(RegexpNameValidator.ID_PATTERN,
83                 Reason.INVALID_GROUPNAME, "Invalid group"));
84     }
85
86     protected User createUser(String aName, String aPassword, Group aGroup)
87         throws UserMgtException {
88         return UsermgtTestUtils.createUser(aName, aPassword, aGroup);
89     }
90
91     /**
92      * Constructs the admin, verify it contains no users and no groups.
93      */
94     public void testConstruct() {
95         assertEquals(0, admin.getUsers().size());
96         assertEquals(0, admin.getGroups().size());
97         assertEquals(0, admin.getUserCount());
98         assertEquals(0, admin.getGroupCount());
99     }
100
101     /**
102      * Creates a new group. Verifies the group is created correctly and that the
103      * user is added.
104      * 
105      */
106     public void testCreateGroup() throws UserMgtException {
107         Group group = admin.createGroup(GROUP1);
108         assertNotNull(group);
109         assertEquals(GROUP1, group.getName());
110
111         Set<Group> groups = admin.getGroups();
112         assertEquals(1, groups.size());
113         assertEquals(1, admin.getGroupCount());
114         assertTrue(groups.contains(group));
115     }
116
117     private void createInvalidGroup(String aUsername) {
118         try {
119             admin.createGroup(aUsername);
120             fail();
121         } catch (UserMgtException e) {
122             assertEquals(UserMgtException.Reason.INVALID_GROUPNAME, e
123                 .getReason());
124             assertEquals(0, admin.getGroupCount());
125         }
126     }
127
128     /**
129      * Creates a new group with an invalid name. Verifies that the appropriate
130      * exception is thrown.
131      * 
132      * @throws UserMgtException
133      */
134     public void testCreateInvalidGroupName() throws UserMgtException {
135         createInvalidGroup("");
136         createInvalidGroup("0abc"); // should not start with digits
137         createInvalidGroup("a b"); // should not contain spaces
138         createInvalidGroup(" aa");
139         createInvalidGroup("aa ");
140     }
141
142     /**
143      * Creates a new group which conflicts with an existing one. Verifies that
144      * the UserMgtException is thrown and that no group is added.
145      * 
146      */
147     public void testCreateDuplicateGroup() throws UserMgtException {
148         admin.createGroup(GROUP1);
149
150         try {
151             admin.createGroup(GROUP1);
152         } catch (UserMgtException e) {
153             assertEquals(UserMgtException.Reason.DUPLICATE_GROUP, e.getReason());
154             assertEquals(1, admin.getGroupCount());
155
156             return;
157         }
158
159         fail();
160     }
161
162     /**
163      * Creates a new user. Verifies the user is created correctly and that the
164      * user is added.
165      * 
166      */
167     public void testCreateUser() throws UserMgtException {
168         Group group = admin.createGroup(GROUP1);
169         User user = admin.createUser(USER1, PASS1, group);
170         assertNotNull(user);
171         assertEquals(USER1, user.getName());
172         user.checkPassword(PASS1);
173
174         Set<User> users = admin.getUsers();
175         assertEquals(1, users.size());
176         assertEquals(1, admin.getUserCount());
177         assertTrue(users.contains(user));
178     }
179
180     private void createInvalidUser(String aUsername, Group aGroup) {
181         try {
182             admin.createUser(aUsername, "pass", aGroup);
183             fail();
184         } catch (UserMgtException e) {
185             assertEquals(UserMgtException.Reason.INVALID_USERNAME, e
186                 .getReason());
187             assertEquals(0, admin.getUserCount());
188         }
189     }
190
191     /**
192      * Constructs users with invalid names. Verifies that the appropriate
193      * exception is thrown.
194      * 
195      */
196     public void testCreateInvalidUserName() throws UserMgtException {
197         Group group = admin.createGroup(GROUP1);
198         createInvalidUser("", group);
199         createInvalidUser("0abc", group); // should not start with digits
200         createInvalidUser("a b", group); // should not contain spaces
201         createInvalidUser(" aa", group);
202         createInvalidUser("aa ", group);
203     }
204
205     /**
206      * Creates a new user which conflicts with an existing one. Verifies that
207      * the UserMgtException is thrown and that no user is added.
208      * 
209      */
210     public void testCreateDuplicateUser() throws UserMgtException {
211         Group group = admin.createGroup(GROUP1);
212         admin.createUser(USER1, PASS1, group);
213
214         try {
215             admin.createUser(USER1, PASS2, group);
216             fail();
217         } catch (UserMgtException e) {
218             assertEquals(UserMgtException.Reason.DUPLICATE_USER, e.getReason());
219             assertEquals(1, admin.getUserCount());
220         }
221     }
222
223     /**
224      * Gets a known user by name. Verifies the correct user is obtained.
225      * Verifies that null is returned when trying to obtain an unknown user.
226      * 
227      */
228     public void testGetUser() throws UserMgtException {
229         Group group = admin.createGroup(GROUP1);
230         User user = admin.createUser(USER1, PASS1, group);
231         User user2 = admin.getUser(USER1);
232         assertTrue(user.equals(user2));
233         assertNull(admin.getUser(USER2));
234     }
235
236     /**
237      * Gets a known group by name. Verifies the correct group is obtained.
238      * Verifies that null is returned when the group is not known.
239      * 
240      */
241     public void testGetGroup() throws UserMgtException {
242         Group group = admin.createGroup(GROUP1);
243         Group group2 = admin.getGroup(GROUP1);
244         assertTrue(group.equals(group2));
245         assertNull(admin.getGroup(GROUP2));
246     }
247
248     /**
249      * Adds a user to a group. Verifies that the user is added using several API
250      * calls. Verifies that an exception occurs if the user is not already part
251      * of the group.
252      * 
253      */
254     public void testAddUserToGroup() throws UserMgtException {
255         Group group = admin.createGroup(GROUP1);
256         User user = admin.createUser(USER1, PASS1, group);
257         Group group2 = admin.createGroup(GROUP2);
258         assertTrue(user.isInGroup(group));
259         assertFalse(user.isInGroup(group2));
260         admin.addUserToGroup(user, group2);
261         assertTrue(user.isInGroup(group));
262         assertTrue(user.isInGroup(group2));
263
264         Set<User> users = admin.getUsers(group2);
265         assertNotNull(users);
266         assertEquals(1, users.size());
267         assertTrue(users.contains(user));
268
269         try {
270             admin.addUserToGroup(user, group);
271         } catch (UserMgtException e) {
272             assertEquals(UserMgtException.Reason.USER_ALREADY_IN_GROUP, e
273                 .getReason());
274
275             return;
276         }
277
278         fail();
279     }
280
281     /**
282      * Adds a user to a group where the user does not exist. Verifies that an
283      * exception occurs.
284      * 
285      */
286     public void testAddUserToGroupUnknownUser() throws UserMgtException {
287         Group group = admin.createGroup(GROUP1);
288         User user = createUser(USER1, PASS1, group);
289
290         try {
291             admin.addUserToGroup(user, group);
292         } catch (UserMgtException e) {
293             assertEquals(UserMgtException.Reason.UNKNOWN_USER, e.getReason());
294
295             return;
296         }
297
298         fail();
299     }
300
301     /**
302      * Adds a user to a group where the user does not exist. Verifies that an
303      * exception occurs.
304      * 
305      */
306     public void testAddUserToGroupUnknownGroup() throws UserMgtException {
307         Group group = admin.createGroup(GROUP1);
308         User user = admin.createUser(USER1, PASS1, group);
309         Group group2 = new Group(GROUP2);
310
311         try {
312             admin.addUserToGroup(user, group2);
313         } catch (UserMgtException e) {
314             assertEquals(UserMgtException.Reason.UNKNOWN_GROUP, e.getReason());
315
316             return;
317         }
318
319         fail();
320     }
321
322     /**
323      * Removes a user from a group. Verifies that the user is removed from the
324      * group using several API calls. Verifies that an exception occurs if the
325      * user not part of the group or if the user is only part of one group.
326      * 
327      */
328     public void testRemoveUserFromGroup() throws UserMgtException {
329         Group group = admin.createGroup(GROUP1);
330
331         User user = admin.createUser(USER1, PASS1, group);
332         Group group2 = admin.createGroup(GROUP2);
333         admin.addUserToGroup(user, group2);
334
335         Set<Group> groups = user.getGroups();
336         assertEquals(2, groups.size());
337         assertTrue(groups.contains(group));
338         assertTrue(groups.contains(group2));
339
340         admin.removeUserFromGroup(user, group);
341         groups = user.getGroups();
342         assertEquals(1, groups.size());
343         assertTrue(groups.contains(group2));
344         assertFalse(groups.contains(group));
345     }
346
347     /**
348      * Removes a user from a group where the user is not known. Verifies that an
349      * exception is thrown.
350      * 
351      */
352     public void testRemoveUserFromGroupUnknownUser() throws UserMgtException {
353         Group group = admin.createGroup(GROUP1);
354         User user = createUser(USER1, GROUP1, group);
355
356         try {
357             admin.removeUserFromGroup(user, group);
358         } catch (UserMgtException e) {
359             assertEquals(UserMgtException.Reason.UNKNOWN_USER, e.getReason());
360         }
361     }
362
363     /**
364      * Removes a user from a group where the group is not known. Verifies that
365      * an exception is thrown.
366      * 
367      */
368     public void testRemoveUserFromGroupUnknownGroup() throws UserMgtException {
369         Group group = admin.createGroup(GROUP1);
370         User user = admin.createUser(USER1, PASS1, group);
371         Group group2 = new Group(GROUP2);
372
373         try {
374             admin.removeUserFromGroup(user, group2);
375         } catch (UserMgtException e) {
376             assertEquals(UserMgtException.Reason.UNKNOWN_GROUP, e.getReason());
377         }
378     }
379
380     /**
381      * Removes a user from a group where the user is only part of one group.
382      * Verifies that an exception is thrown.
383      * 
384      */
385     public void testRemoveUserFromGroupOnlyGroup() throws UserMgtException {
386         Group group = admin.createGroup(GROUP1);
387         User user = admin.createUser(USER1, PASS1, group);
388
389         try {
390             admin.removeUserFromGroup(user, group);
391         } catch (UserMgtException e) {
392             assertEquals(UserMgtException.Reason.USER_MUST_BE_IN_A_GROUP, e
393                 .getReason());
394         }
395     }
396
397     /**
398      * Gets the list of users and groups. Verifies that the correct suers and
399      * groups are returned. Verifies also that the relations from user to group
400      * are correct.
401      * 
402      */
403     public void testGetUsersAndGroups() throws UserMgtException {
404         Group group1 = admin.createGroup(GROUP1);
405         Group group2 = admin.createGroup(GROUP2);
406
407         User user1 = admin.createUser(USER1, PASS1, group1);
408         admin.addUserToGroup(user1, group2);
409
410         User user2 = admin.createUser(USER2, PASS2, group2);
411
412         Set<User> users = admin.getUsers();
413         assertEquals(2, users.size());
414         assertTrue(users.contains(user1));
415         assertTrue(users.contains(user2));
416
417         Set<Group> groups = admin.getGroups();
418         assertEquals(2, groups.size());
419         assertTrue(groups.contains(group1));
420         assertTrue(groups.contains(group2));
421
422         assertTrue(user1.isInGroup(group1));
423         assertTrue(user1.isInGroup(group2));
424         assertFalse(user2.isInGroup(group1));
425         assertTrue(user2.isInGroup(group2));
426
427         Set<Group> groups1 = user1.getGroups();
428         assertEquals(2, groups1.size());
429
430         Set<Group> groups2 = user2.getGroups();
431         assertEquals(1, groups2.size());
432     }
433
434     /**
435      * Renames a user. Verifies that the user is renamed. Verifies that
436      * exceptions are thrown when an attempt is made to rename the user to
437      * itself or to another existing user, or when the group does not exist.
438      * 
439      */
440     public void testRenameUser() throws UserMgtException {
441         Group group = admin.createGroup(GROUP1);
442         User user1 = admin.createUser(USER1, PASS1, group);
443         admin.renameUser(user1, USER2);
444         assertEquals(USER2, user1.getName());
445         assertEquals(user1, admin.getUser(USER2));
446
447         try {
448             admin.renameUser(user1, USER1);
449         } catch (UserMgtException e) {
450             assertEquals(UserMgtException.Reason.DUPLICATE_USER, e.getReason());
451
452             // do a trivial reanem
453             try {
454                 admin.renameUser(user1, user1.getName());
455             } catch (UserMgtException e2) {
456                 assertEquals(UserMgtException.Reason.TRIVIAL_RENAME, e2
457                     .getReason());
458
459                 return;
460             }
461
462             fail();
463         }
464
465         fail();
466     }
467
468     /**
469      * Renames a user to a user with an invalid username. Verifies that the
470      * appropriate exception is thrown.
471      * 
472      */
473     public void testRenameUserInvalidUsername() throws UserMgtException {
474         Group group = admin.createGroup(GROUP1);
475         User user1 = admin.createUser(USER1, PASS1, group);
476
477         try {
478             admin.renameUser(user1, USER2);
479         } catch (UserMgtException e) {
480             assertEquals(e.getReason(), Reason.INVALID_USERNAME);
481         }
482     }
483
484     /**
485      * Renames a group. Verifies that the group is renamed. Verifies that
486      * exceptions are thrown when an attempt is made to rename the group to
487      * itself or to another existing group or when the group does not exist.
488      * 
489      */
490     public void testRenameGroup() throws UserMgtException {
491         Group group = admin.createGroup(GROUP1);
492         admin.renameGroup(group, GROUP2);
493         assertEquals(GROUP2, group.getName());
494         assertEquals(group, admin.getGroup(GROUP2));
495
496         admin.createGroup(GROUP1);
497
498         try {
499             admin.renameGroup(group, GROUP1);
500         } catch (UserMgtException e) {
501             assertEquals(UserMgtException.Reason.DUPLICATE_GROUP, e.getReason());
502
503             // do a trivial reanem
504             try {
505                 admin.renameGroup(group, group.getName());
506             } catch (UserMgtException e2) {
507                 assertEquals(UserMgtException.Reason.TRIVIAL_RENAME, e2
508                     .getReason());
509
510                 return;
511             }
512
513             fail();
514
515             return;
516         }
517
518         fail();
519     }
520
521     /**
522      * Renames a group to a group with an invalid name. Verifies that the
523      * appropriate exception is thrown.
524      * 
525      */
526     public void testRenameGroupInvalidGroupname() throws UserMgtException {
527         Group group = admin.createGroup(GROUP1);
528
529         try {
530             admin.renameGroup(group, "a b");
531         } catch (UserMgtException e) {
532             assertEquals(e.getReason(), Reason.INVALID_GROUPNAME);
533         }
534     }
535
536     /**
537      * Removes a user. Verifies that the user is removed. Verifies that the an
538      * exception is thrown when the user does not exist.
539      * 
540      */
541     public void testRemoveUser() throws UserMgtException {
542         Group group = admin.createGroup(GROUP1);
543         User user = admin.createUser(USER1, PASS1, group);
544
545         assertEquals(1, admin.getUserCount());
546         admin.removeUser(user);
547         assertEquals(0, admin.getUserCount());
548
549         admin.createUser(USER1, PASS1, group);
550         assertEquals(1, admin.getUserCount());
551
552         User user2 = createUser(USER2, PASS2, group);
553
554         try {
555             admin.removeUser(user2);
556         } catch (UserMgtException e) {
557             assertEquals(UserMgtException.Reason.UNKNOWN_USER, e.getReason());
558         }
559     }
560
561     /**
562      * Removes a group. Verifies that the group is removed. Verifies that the an
563      * exception is thrown when the group does not exist or if there are still
564      * users in the group.
565      * 
566      */
567     public void testRemoveGroup() throws UserMgtException {
568         Group group1 = admin.createGroup(GROUP1);
569         assertEquals(1, admin.getGroupCount());
570         admin.removeGroup(group1);
571         assertEquals(0, admin.getGroupCount());
572         group1 = admin.createGroup(GROUP1);
573
574         admin.createUser(USER1, PASS1, group1);
575
576         try {
577             admin.removeGroup(group1);
578         } catch (UserMgtException e) {
579             assertEquals(UserMgtException.Reason.GROUP_STILL_OCCUPIED, e
580                 .getReason());
581
582             return;
583         }
584
585         fail();
586     }
587
588     /**
589      * Tries to remove an unknown group. Verifies that an exception is thrown.
590      * 
591      */
592     public void testRemoveGroupUnknownGroup() throws UserMgtException {
593         Group group = admin.createGroup(GROUP1);
594         Group group2 = new Group(GROUP2);
595
596         try {
597             admin.removeGroup(group2);
598         } catch (UserMgtException e) {
599             assertEquals(UserMgtException.Reason.UNKNOWN_GROUP, e.getReason());
600         }
601     }
602
603     /**
604      * Changes the password, verifies that this succeeds.
605      * 
606      * @throws UserMgtException
607      */
608     public void testChangePassword() throws UserMgtException {
609         Group group = admin.createGroup(GROUP1);
610         User user = admin.createUser(USER1, PASS1, group);
611         user.changePassword(PASS1, PASS2);
612
613         // retrieve the user and verifies the password hasn't changed.
614         User user2 = admin.getUser(USER1);
615
616         try {
617             user2.checkPassword(PASS2);
618             fail(); // password should not have changed already.
619         } catch (UserMgtException e) {
620             // ok.
621         }
622
623         // now notify the admin of the change in the user
624         admin.userModified(user);
625
626         user2 = admin.getUser(USER1);
627         user2.checkPassword(PASS2); // this time it should succeed.
628     }
629
630     /**
631      * Performance test. Finds a user by name.
632      * 
633      */
634     public void testPerformanceFindUserByName() throws UserMgtException {
635         Group group = admin.createGroup(GROUP1);
636         admin.createUser(USER1, PASS1, group);
637
638         int n = 1000;
639         long time = System.currentTimeMillis();
640
641         for (int i = 0; i < n; i++) {
642             admin.getUser(USER1);
643         }
644
645         LOGGER.info("Looked up a user " + n + " times in " +
646             ((float) (System.currentTimeMillis() - time) / 1000.0));
647     }
648 }