Modified: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/MembershipProviderTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/MembershipProviderTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/MembershipProviderTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/MembershipProviderTest.java Wed Aug 17 14:21:07 2016 @@ -16,17 +16,25 @@ */ package org.apache.jackrabbit.oak.security.user; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Set; - +import javax.annotation.Nonnull; import javax.jcr.RepositoryException; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import com.google.common.collect.Maps; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.oak.AbstractSecurityTest; +import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; @@ -39,56 +47,53 @@ import org.junit.Test; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; /** * Tests large group and user graphs. * <ul> - * <li>{@link #NUM_USERS} users</li> - * <li>{@link #NUM_GROUPS} groups</li> - * <li>1 group with all users</li> - * <li>1 user with all groups</li> + * <li>{@link #NUM_USERS} users</li> + * <li>{@link #NUM_GROUPS} groups</li> + * <li>1 group with all users</li> + * <li>1 user with all groups</li> * </ul> * * @since OAK 1.0 */ -public class MembershipProviderTest extends AbstractSecurityTest { +public class MembershipProviderTest extends AbstractSecurityTest implements UserConstants { private static final int NUM_USERS = 1000; private static final int NUM_GROUPS = 1000; private static final int SIZE_TH = 10; private UserManagerImpl userMgr; + private MembershipProvider mp; private Set<String> testUsers = new HashSet<String>(); private Set<String> testGroups = new HashSet<String>(); @Before public void before() throws Exception { super.before(); - testUsers.clear(); - testGroups.clear(); userMgr = new UserManagerImpl(root, namePathMapper, getSecurityProvider()); - // set the membership size threshold low for testing - userMgr.getMembershipProvider().setMembershipSizeThreshold(SIZE_TH); + mp = userMgr.getMembershipProvider(); + // set the threshold low for testing + mp.setMembershipSizeThreshold(SIZE_TH); } @After public void after() throws Exception { try { - for (String authId: testGroups) { - Authorizable auth = userMgr.getAuthorizable(authId); - if (auth != null) { - auth.remove(); - } - } - for (String authId: testUsers) { - Authorizable auth = userMgr.getAuthorizable(authId); + for (String path : Iterables.concat(testUsers, testGroups)) { + Authorizable auth = userMgr.getAuthorizableByPath(path); if (auth != null) { auth.remove(); } } root.commit(); } finally { + testUsers.clear(); + testGroups.clear(); super.after(); } } @@ -96,24 +101,24 @@ public class MembershipProviderTest exte @Test public void testManyMembers() throws Exception { Set<String> members = new HashSet<String>(); - Group grp = createGroup(); - for (int i=0; i<NUM_USERS; i++) { + Group grp = createGroup(); + for (int i = 0; i < NUM_USERS; i++) { User usr = createUser(); grp.addMember(usr); members.add(usr.getID()); } root.commit(); - checkMembers(grp, members); + assertMembers(grp, members); // also check storage structure Tree tree = root.getTree(grp.getPath()); assertEquals( "rep:members property must have correct number of references", SIZE_TH, - tree.getProperty(UserConstants.REP_MEMBERS).count() + tree.getProperty(REP_MEMBERS).count() ); - Tree membersList = tree.getChild(UserConstants.REP_MEMBERS_LIST); + Tree membersList = tree.getChild(REP_MEMBERS_LIST); assertTrue( "rep:memberList must exist", membersList.exists() @@ -121,7 +126,7 @@ public class MembershipProviderTest exte assertEquals( "rep:memberList must have correct primary type.", - UserConstants.NT_REP_MEMBER_REFERENCES_LIST, + NT_REP_MEMBER_REFERENCES_LIST, membersList.getProperty(JcrConstants.JCR_PRIMARYTYPE).getValue(Type.STRING) ); @@ -136,7 +141,7 @@ public class MembershipProviderTest exte public void testManyMemberships() throws Exception { Set<String> memberships = new HashSet<String>(); User usr = createUser(); - for (int i=0; i<NUM_GROUPS; i++) { + for (int i = 0; i < NUM_GROUPS; i++) { Group grp = createGroup(); grp.addMember(usr); memberships.add(grp.getID()); @@ -155,17 +160,17 @@ public class MembershipProviderTest exte public void testNestedMembers() throws Exception { Set<String> members = new HashSet<String>(); Set<String> declaredMembers = new HashSet<String>(); - Group grp = createGroup(); - for (int i=0; i<10; i++) { + Group grp = createGroup(); + for (int i = 0; i < 10; i++) { Group g1 = createGroup(); grp.addMember(g1); members.add(g1.getID()); declaredMembers.add(g1.getID()); - for (int j=0; j<10; j++) { + for (int j = 0; j < 10; j++) { Group g2 = createGroup(); g1.addMember(g2); members.add(g2.getID()); - for (int k=0; k<10; k++) { + for (int k = 0; k < 10; k++) { User usr = createUser(); g2.addMember(usr); members.add(usr.getID()); @@ -174,7 +179,7 @@ public class MembershipProviderTest exte } root.commit(); - checkMembers(grp, members); + assertMembers(grp, members); Iterator<Authorizable> iter = grp.getDeclaredMembers(); while (iter.hasNext()) { @@ -187,14 +192,14 @@ public class MembershipProviderTest exte @Test public void testNestedMemberships() throws Exception { Set<String> memberships = new HashSet<String>(); - User user = createUser(); + User user = createUser(); Group grp = createGroup(); memberships.add(grp.getID()); - for (int i=0; i<10; i++) { + for (int i = 0; i < 10; i++) { Group g1 = createGroup(); grp.addMember(g1); memberships.add(g1.getID()); - for (int j=0; j<10; j++) { + for (int j = 0; j < 10; j++) { Group g2 = createGroup(); g1.addMember(g2); memberships.add(g2.getID()); @@ -215,8 +220,8 @@ public class MembershipProviderTest exte public void testRemoveMembers() throws Exception { Set<String> members = new HashSet<String>(); String[] users = new String[NUM_USERS]; - Group grp = createGroup(); - for (int i=0; i<NUM_USERS; i++) { + Group grp = createGroup(); + for (int i = 0; i < NUM_USERS; i++) { User usr = createUser(); grp.addMember(usr); members.add(usr.getID()); @@ -226,24 +231,24 @@ public class MembershipProviderTest exte // remove the first TH users, this should remove all references from rep:members in the group node and remove // the rep:members property - for (int i=0; i<SIZE_TH; i++) { + for (int i = 0; i < SIZE_TH; i++) { Authorizable auth = userMgr.getAuthorizable(users[i]); members.remove(users[i]); grp.removeMember(auth); } root.commit(); - checkMembers(grp, members); + assertMembers(grp, members); // also check storage structure Tree tree = root.getTree(grp.getPath()); assertNull( "rep:members property not exist", - tree.getProperty(UserConstants.REP_MEMBERS) + tree.getProperty(REP_MEMBERS) ); // now add TH/2 again. - for (int i=0; i<SIZE_TH/2; i++) { + for (int i = 0; i < SIZE_TH / 2; i++) { Authorizable auth = userMgr.getAuthorizable(users[i]); members.add(users[i]); grp.addMember(auth); @@ -253,47 +258,45 @@ public class MembershipProviderTest exte assertEquals( "rep:members property must have correct number of references", SIZE_TH / 2, - tree.getProperty(UserConstants.REP_MEMBERS).count() + tree.getProperty(REP_MEMBERS).count() ); - checkMembers(grp, members); + assertMembers(grp, members); // now remove the users 20-30, this should remove the 2nd overflow node - for (int i=2*SIZE_TH; i< (3*SIZE_TH); i++) { + for (int i = 2 * SIZE_TH; i < (3 * SIZE_TH); i++) { Authorizable auth = userMgr.getAuthorizable(users[i]); members.remove(users[i]); grp.removeMember(auth); } root.commit(); - checkMembers(grp, members); + assertMembers(grp, members); - Tree membersList = tree.getChild(UserConstants.REP_MEMBERS_LIST); + Tree membersList = tree.getChild(REP_MEMBERS_LIST); assertFalse( "the first overflow node must not exist", membersList.getChild("1").exists() ); // now add 10 users and check if the "1" node exists again - for (int i=2*SIZE_TH; i< (3*SIZE_TH); i++) { + for (int i = 2 * SIZE_TH; i < (3 * SIZE_TH); i++) { Authorizable auth = userMgr.getAuthorizable(users[i]); members.add(users[i]); grp.addMember(auth); } root.commit(); - checkMembers(grp, members); + assertMembers(grp, members); - membersList = tree.getChild(UserConstants.REP_MEMBERS_LIST); - assertTrue( - "the first overflow node must not exist", - membersList.getChild("1").exists() + membersList = tree.getChild(REP_MEMBERS_LIST); + assertTrue("the first overflow node must not exist", membersList.getChild("1").exists() ); } @Test public void testAddMembersAgain() throws Exception { Set<String> members = new HashSet<String>(); - Group grp = createGroup(); - for (int i=0; i<NUM_USERS; i++) { + Group grp = createGroup(); + for (int i = 0; i < NUM_USERS; i++) { User usr = createUser(); grp.addMember(usr); members.add(usr.getID()); @@ -308,45 +311,271 @@ public class MembershipProviderTest exte @Test public void testAddMembersAgainOnMembershipProvider() throws Exception { Set<String> memberPaths = new HashSet<String>(); - Group grp = createGroup(); - for (int i=0; i<NUM_USERS; i++) { + Group grp = createGroup(); + for (int i = 0; i < NUM_USERS; i++) { User usr = createUser(); grp.addMember(usr); memberPaths.add(usr.getPath()); } root.commit(); - - MembershipProvider mp = userMgr.getMembershipProvider(); Tree groupTree = root.getTree(grp.getPath()); for (String path : memberPaths) { Tree memberTree = root.getTree(path); assertFalse(mp.addMember(groupTree, memberTree)); - assertFalse(mp.addMember(groupTree, TreeUtil.getString(memberTree, JcrConstants.JCR_UUID))); + assertFalse(mp.addMember(groupTree, memberTree)); + + String memberId = TreeUtil.getString(memberTree, REP_AUTHORIZABLE_ID); + Map<String, String> m = new HashMap<String, String>(1); + m.put(TreeUtil.getString(memberTree, JcrConstants.JCR_UUID), memberId); + + Set<String> failed = mp.addMembers(groupTree, m); + assertFalse(failed.isEmpty()); + assertTrue(failed.contains(memberId)); + } + } + + @Test + public void testIsDeclaredMemberTransient() throws Exception { + Group g = createGroup(); + List<Authorizable> members = createMembers(g, NUM_USERS/2); + + Tree groupTree = getTree(g); + + // test declared members with transient modifications + for (Authorizable a : members) { + assertTrue(mp.isDeclaredMember(groupTree, getTree(a))); + } + } + + @Test + public void testIsDeclaredMember() throws Exception { + Group g = createGroup(); + List<Authorizable> members = createMembers(g, NUM_USERS/2); + root.commit(); + + Tree groupTree = getTree(g); + + // test declared members after commit + for (Authorizable a : members) { + assertTrue(mp.isDeclaredMember(groupTree, getTree(a))); + } + } + + @Test + public void testIsDeclaredMemberFew() throws Exception { + Group g = createGroup(); + Group m1 = createGroup(); + User m2 = createUser(); + + g.addMembers(m1.getID(), m2.getID()); + + Tree groupTree = getTree(g); + assertFalse(groupTree.hasChild(REP_MEMBERS_LIST)); + + // test declared members with transient modifications + assertTrue(mp.isDeclaredMember(groupTree, getTree(m1))); + assertTrue(mp.isDeclaredMember(groupTree, getTree(m2))); + + // ... and after commit + root.commit(); + assertTrue(mp.isDeclaredMember(groupTree, getTree(m1))); + assertTrue(mp.isDeclaredMember(groupTree, getTree(m2))); + } + + @Test + public void testIsMemberTransient() throws Exception { + Group g = createGroup(); + Group g2 = createGroup(); + g.addMember(g2); + + List<Authorizable> members = createMembers(g2, 50); + + Tree groupTree = getTree(g); + + // test members with transient modifications + for (Authorizable a : members) { + Tree tree = getTree(a); + assertFalse(mp.isDeclaredMember(groupTree, tree)); + assertTrue(mp.isMember(groupTree, tree)); + } + } + + @Test + public void testIsMember() throws Exception { + Group g = createGroup(); + Group g2 = createGroup(); + g.addMember(g2); + + List<Authorizable> members = createMembers(g2, NUM_USERS/2); + root.commit(); + + // test members after commit + Tree groupTree = getTree(g); + for (Authorizable a : members) { + Tree tree = getTree(a); + assertFalse(mp.isDeclaredMember(groupTree, tree)); + assertTrue(mp.isMember(groupTree, tree)); } } + @Test + public void testIsMemberFew() throws Exception { + Group g = createGroup(); + Group g2 = createGroup(); + g.addMember(g2); + + User m1 = createUser(); + Group m2 = createGroup(); + g2.addMember(m1); + g2.addMember(m2); + + Tree groupTree = getTree(g); + + // test members with transient modifications + assertFalse(mp.isDeclaredMember(groupTree, getTree(m1))); + assertFalse(mp.isDeclaredMember(groupTree, getTree(m2))); + assertTrue(mp.isMember(groupTree, getTree(m1))); + assertTrue(mp.isMember(groupTree, getTree(m2))); + + // ... and after commit + root.commit(); + assertFalse(mp.isDeclaredMember(groupTree, getTree(m1))); + assertFalse(mp.isDeclaredMember(groupTree, getTree(m2))); + assertTrue(mp.isMember(groupTree, getTree(m1))); + assertTrue(mp.isMember(groupTree, getTree(m2))); + } + + @Test + public void testTransientInMembersList() throws Exception { + Group g = createGroup(); + createMembers(g, NUM_USERS/2); + root.commit(); + + // add another transient member that will end up in the members-ref-list + User u = createUser(); + g.addMember(u); + + Tree groupTree = getTree(g); + Tree memberTree = getTree(u); + + assertTrue(mp.isDeclaredMember(groupTree, memberTree)); + assertTrue(mp.isMember(groupTree, memberTree)); + + assertFalse(Iterators.contains(mp.getMembership(memberTree, false), groupTree.getPath())); + assertFalse(Iterators.contains(mp.getMembership(memberTree, true), groupTree.getPath())); + root.commit(); + assertTrue(Iterators.contains(mp.getMembership(memberTree, false), groupTree.getPath())); + assertTrue(Iterators.contains(mp.getMembership(memberTree, true), groupTree.getPath())); + } + + @Test + public void testNoMember() throws Exception { + Group g = createGroup(); + Group notMember = createGroup(); + User notMember2 = createUser(); + + assertFalse(g.isDeclaredMember(notMember)); + assertFalse(g.isDeclaredMember(notMember2)); + + assertFalse(g.isMember(notMember)); + assertFalse(g.isMember(notMember2)); + + root.commit(); + + assertFalse(g.isDeclaredMember(notMember)); + assertFalse(g.isDeclaredMember(notMember2)); + + assertFalse(g.isMember(notMember)); + assertFalse(g.isMember(notMember2)); + } + + @Test + public void testAddMembersExceedThreshold() throws Exception { + Tree groupTree = root.getTree(createGroup().getPath()); + + // 1. add array of 21 memberIDs exceeding the threshold + Map<String, String> memberIds = createIdMap(0, 21); + mp.addMembers(groupTree, memberIds); + + PropertyState repMembers = groupTree.getProperty(REP_MEMBERS); + assertNotNull(repMembers); + assertEquals(SIZE_TH, repMembers.count()); + + // the members-list must how have two ref-members nodes one with 10 and + // one with a single ref-value + assertMemberList(groupTree, 2, 1); + + // 2. add more members without reaching threshold => still 2 ref-nodes + memberIds = createIdMap(21, 29); + mp.addMembers(groupTree, memberIds); + assertMemberList(groupTree, 2, 9); + + // 3. fill up second ref-members node => a new one must be created + memberIds = createIdMap(29, 35); + mp.addMembers(groupTree, memberIds); + assertMemberList(groupTree, 3, 5); + + // 4. remove members from the initial set => ref nodes as before, rep:members prop on group modified + memberIds.clear(); + memberIds.put(MembershipProvider.getContentID("member1"), "member1"); + memberIds.put(MembershipProvider.getContentID("member2"), "member2"); + mp.removeMembers(groupTree, Maps.newHashMap(memberIds)); + + assertMemberList(groupTree, 3, 5); + assertEquals(8, groupTree.getProperty(REP_MEMBERS).count()); + + // 5. add members again => best-tree is the ref-member-node + memberIds = createIdMap(35, 39); + mp.addMembers(groupTree, memberIds); + + assertEquals(8, groupTree.getProperty(REP_MEMBERS).count()); + assertMemberList(groupTree, 3, 9); + + // 6. adding more members will fill up rep:members again and create new ref-node + memberIds = createIdMap(39, 45); + mp.addMembers(groupTree, memberIds); + + assertEquals(SIZE_TH, groupTree.getProperty(REP_MEMBERS).count()); + assertEquals(4, groupTree.getChild(REP_MEMBERS_LIST).getChildrenCount(10)); + } + private User createUser() throws RepositoryException { String userId = "testUser" + testUsers.size(); User usr = userMgr.createUser(userId, "pw"); - testUsers.add(userId); - if ((testUsers.size() + testGroups.size()) % 100 == 0) { - System.out.println("created " + testGroups.size() + " groups, " + testUsers.size() + " users."); - } + testUsers.add(usr.getPath()); return usr; } private Group createGroup() throws RepositoryException { String groupName = "testGroup" + testGroups.size(); Group grp = userMgr.createGroup(groupName); - testGroups.add(groupName); - if ((testUsers.size() + testGroups.size()) % 100 == 0) { - System.out.println("created " + testGroups.size() + " groups, " + testUsers.size() + " users."); - } + testGroups.add(grp.getPath()); return grp; } - private void checkMembers(Group grp, Set<String> ms) throws RepositoryException { + private List<Authorizable> createMembers(@Nonnull Group g, int cnt) throws Exception { + List<Authorizable> members = new ArrayList(); + for (int i = 0; i <= cnt; i++) { + User u = createUser(); + Group gr = createGroup(); + g.addMembers(u.getID(), gr.getID()); + members.add(u); + members.add(gr); + } + return members; + } + + private static Map<String, String> createIdMap(int start, int end) { + Map<String, String> memberIds = Maps.newLinkedHashMap(); + for (int i = start; i < end; i++) { + String memberId = "member" + i; + memberIds.put(MembershipProvider.getContentID(memberId), memberId); + } + return memberIds; + } + + private static void assertMembers(Group grp, Set<String> ms) throws RepositoryException { Set<String> members = new HashSet<String>(ms); Iterator<Authorizable> iter = grp.getMembers(); while (iter.hasNext()) { @@ -355,4 +584,19 @@ public class MembershipProviderTest exte } assertEquals("Group must have all members", 0, members.size()); } + + private static void assertMemberList(@Nonnull Tree groupTree, int cntRefTrees, int cnt) { + Tree list = groupTree.getChild(REP_MEMBERS_LIST); + assertTrue(list.exists()); + assertEquals(cntRefTrees, list.getChildrenCount(5)); + for (Tree c : list.getChildren()) { + PropertyState repMembers = c.getProperty(REP_MEMBERS); + assertNotNull(repMembers); + assertTrue(SIZE_TH == repMembers.count() || cnt == repMembers.count()); + } + } + + private Tree getTree(@Nonnull Authorizable a) throws Exception { + return root.getTree(a.getPath()); + } } \ No newline at end of file
Copied: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java (from r1694049, jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java) URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java?p2=jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java&p1=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java&r1=1694049&r2=1756639&rev=1756639&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/RemoveMembersByIdBestEffortTest.java Wed Aug 17 14:21:07 2016 @@ -62,4 +62,20 @@ public class RemoveMembersByIdBestEffort root.refresh(); assertFalse(testGroup.isMember(memberGroup)); } + + @Test + public void testMemberListExistingMembers() throws Exception { + MembershipProvider mp = ((UserManagerImpl) getUserManager(root)).getMembershipProvider(); + try { + mp.setMembershipSizeThreshold(5); + for (int i = 0; i < 10; i++) { + testGroup.addMembers("member" + i); + } + + Set<String> failed = testGroup.removeMembers("member8"); + assertTrue(failed.isEmpty()); + } finally { + mp.setMembershipSizeThreshold(100); // back to default + } + } } \ No newline at end of file Modified: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserInitializerTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserInitializerTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserInitializerTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserInitializerTest.java Wed Aug 17 14:21:07 2016 @@ -130,6 +130,13 @@ public class UserInitializerTest extends assertArrayEquals( new String[]{UserConstants.NT_REP_AUTHORIZABLE}, Iterables.toArray(declaringNtNames, String.class)); + + Tree repMembers = oakIndex.getChild("repMembers"); + assertIndexDefinition(repMembers, UserConstants.REP_MEMBERS, false); + declaringNtNames = TreeUtil.getStrings(repMembers, IndexConstants.DECLARING_NODE_TYPES); + assertArrayEquals( + new String[]{UserConstants.NT_REP_MEMBER_REFERENCES}, + Iterables.toArray(declaringNtNames, String.class)); } /** Modified: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java Wed Aug 17 14:21:07 2016 @@ -48,7 +48,6 @@ import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; @@ -320,9 +319,41 @@ public class UserValidatorTest extends A group1.addMember(group2); group2.addMember(group3); - - assertFalse(group3.addMember(group1)); - + + // manually create the cyclic membership + Tree group3Tree = root.getTree(group3.getPath()); + Set<String> values = Collections.singleton(root.getTree(group1.getPath()).getProperty(JcrConstants.JCR_UUID).getValue(Type.STRING)); + PropertyState prop = PropertyStates.createProperty(REP_MEMBERS, values, Type.WEAKREFERENCES); + group3Tree.setProperty(prop); + root.commit(); + fail("Cyclic group membership must be detected"); + } catch (CommitFailedException e) { + // success + } finally { + if (group1 != null) group1.remove(); + if (group2 != null) group2.remove(); + if (group3 != null) group3.remove(); + root.commit(); + } + } + + @Test + public void testDetectCyclicMembershipWithIntermediateCommit() throws Exception { + Group group1 = null; + Group group2 = null; + Group group3 = null; + + UserManager userMgr = getUserManager(root); + try { + group1 = userMgr.createGroup("group1"); + group2 = userMgr.createGroup("group2"); + group3 = userMgr.createGroup("group3"); + + group1.addMember(group2); + group2.addMember(group3); + root.commit(); + + // manually create the cyclic membership Tree group3Tree = root.getTree(group3.getPath()); Set<String> values = Collections.singleton(root.getTree(group1.getPath()).getProperty(JcrConstants.JCR_UUID).getValue(Type.STRING)); PropertyState prop = PropertyStates.createProperty(REP_MEMBERS, values, Type.WEAKREFERENCES); Modified: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java Wed Aug 17 14:21:07 2016 @@ -248,4 +248,23 @@ public class AutoSaveEnabledManagerTest assertFalse(root.hasPendingChanges()); assertFalse(u.declaredMemberOf().hasNext()); } + + @Test + public void testAddMembers() throws Exception { + User u = mgr.createUser("u", "u"); + Group g = mgr.createGroup("g"); + + assertTrue(g.addMembers(u.getID()).isEmpty()); + assertFalse(root.hasPendingChanges()); + } + + @Test + public void testRemoveMembers() throws Exception { + User u = mgr.createUser("u", "u"); + Group g = mgr.createGroup("g"); + g.addMember(u); + + assertTrue(g.removeMembers(u.getID()).isEmpty()); + assertFalse(root.hasPendingChanges()); + } } \ No newline at end of file Copied: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java (from r1735564, jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java) URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java?p2=jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java&p1=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java&r1=1735564&r2=1756639&rev=1756639&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionBestEffortTest.java Wed Aug 17 14:21:07 2016 @@ -16,13 +16,14 @@ */ package org.apache.jackrabbit.oak.spi.security.user.action; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import org.apache.jackrabbit.oak.spi.xml.ImportBehavior; import org.junit.Test; -import java.util.List; +import java.util.Set; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -30,7 +31,7 @@ public class GroupActionBestEffortTest e @Test public void testMembersAddedNonExisting() throws Exception { - List<String> nonExisting = ImmutableList.of("blinder", "passagier"); + Set<String> nonExisting = ImmutableSet.of("blinder", "passagier"); testGroup.addMembers(nonExisting.toArray(new String[nonExisting.size()])); assertTrue(Iterables.elementsEqual(nonExisting, groupAction.memberIds)); @@ -39,11 +40,11 @@ public class GroupActionBestEffortTest e @Test public void testMembersRemovedNonExisting() throws Exception { - List<String> nonExisting = ImmutableList.of("blinder", "passagier"); + Set<String> nonExisting = ImmutableSet.of("blinder", "passagier"); testGroup.removeMembers(nonExisting.toArray(new String[nonExisting.size()])); assertFalse(groupAction.memberIds.iterator().hasNext()); - assertTrue(Iterables.elementsEqual(nonExisting, groupAction.failedIds)); + assertEquals(nonExisting, groupAction.failedIds); } @Override Copied: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java (from r1735564, jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java) URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java?p2=jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java&p1=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java&r1=1735564&r2=1756639&rev=1756639&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/GroupActionTest.java Wed Aug 17 14:21:07 2016 @@ -17,6 +17,7 @@ package org.apache.jackrabbit.oak.spi.security.user.action; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; @@ -41,6 +42,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.jcr.RepositoryException; import java.util.List; +import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -113,20 +115,20 @@ public class GroupActionTest extends Abs testUser02 = getUserManager(root).createUser(TEST_USER_PREFIX + "02", ""); testGroup.addMember(testUser02); - List<String> memberIds = ImmutableList.of(testUser01.getID()); - List<String> failedIds = ImmutableList.of(testUser02.getID(), testGroup.getID()); + Set<String> memberIds = ImmutableSet.of(testUser01.getID()); + Set<String> failedIds = ImmutableSet.of(testUser02.getID(), testGroup.getID()); Iterable<String> ids = Iterables.concat(memberIds, failedIds); testGroup.addMembers(Iterables.toArray(ids, String.class)); assertTrue(groupAction.onMembersAddedCalled); assertEquals(testGroup, groupAction.group); - assertTrue(Iterables.elementsEqual(memberIds, groupAction.memberIds)); - assertTrue(Iterables.elementsEqual(failedIds, groupAction.failedIds)); + assertEquals(memberIds, groupAction.memberIds); + assertEquals(failedIds, groupAction.failedIds); } @Test public void testMembersAddedNonExisting() throws Exception { - List<String> nonExisting = ImmutableList.of("blinder", "passagier"); + Set<String> nonExisting = ImmutableSet.of("blinder", "passagier"); testGroup.addMembers(nonExisting.toArray(new String[nonExisting.size()])); assertFalse(groupAction.memberIds.iterator().hasNext()); @@ -139,20 +141,20 @@ public class GroupActionTest extends Abs testUser02 = getUserManager(root).createUser(TEST_USER_PREFIX + "02", ""); testGroup.addMember(testUser01); - List<String> memberIds = ImmutableList.of(testUser01.getID()); - List<String> failedIds = ImmutableList.of(testUser02.getID(), testGroup.getID()); + Set<String> memberIds = ImmutableSet.of(testUser01.getID()); + Set<String> failedIds = ImmutableSet.of(testUser02.getID(), testGroup.getID()); Iterable<String> ids = Iterables.concat(memberIds, failedIds); testGroup.removeMembers(Iterables.toArray(ids, String.class)); assertTrue(groupAction.onMembersRemovedCalled); assertEquals(testGroup, groupAction.group); - assertTrue(Iterables.elementsEqual(memberIds, groupAction.memberIds)); - assertTrue(Iterables.elementsEqual(failedIds, groupAction.failedIds)); + assertEquals(memberIds, groupAction.memberIds); + assertEquals(failedIds, groupAction.failedIds); } @Test public void testMembersRemovedNonExisting() throws Exception { - List<String> nonExisting = ImmutableList.of("blinder", "passagier"); + Set<String> nonExisting = ImmutableSet.of("blinder", "passagier"); testGroup.removeMembers(nonExisting.toArray(new String[nonExisting.size()])); assertFalse(groupAction.memberIds.iterator().hasNext()); @@ -175,8 +177,8 @@ public class GroupActionTest extends Abs boolean onMembersRemovedCalled = false; Group group; - Iterable<String> memberIds; - Iterable<String> failedIds; + Set<String> memberIds; + Set<String> failedIds; Authorizable member; @Override @@ -189,8 +191,8 @@ public class GroupActionTest extends Abs @Override public void onMembersAdded(@Nonnull Group group, @Nonnull Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException { this.group = group; - this.memberIds = memberIds; - this.failedIds = failedIds; + this.memberIds = ImmutableSet.copyOf(memberIds); + this.failedIds = ImmutableSet.copyOf(failedIds); onMembersAddedCalled = true; } @@ -204,8 +206,8 @@ public class GroupActionTest extends Abs @Override public void onMembersRemoved(@Nonnull Group group, @Nonnull Iterable<String> memberIds, @Nonnull Iterable<String> failedIds, @Nonnull Root root, @Nonnull NamePathMapper namePathMapper) throws RepositoryException { this.group = group; - this.memberIds = memberIds; - this.failedIds = failedIds; + this.memberIds = ImmutableSet.copyOf(memberIds); + this.failedIds = ImmutableSet.copyOf(failedIds); onMembersRemovedCalled = true; } } Modified: jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/action/PasswordValidationActionTest.java Wed Aug 17 14:21:07 2016 @@ -33,6 +33,7 @@ import org.apache.jackrabbit.oak.securit import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.SecurityProvider; +import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider; import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil; @@ -200,6 +201,12 @@ public class PasswordValidationActionTes return ConfigurationParameters.of(super.getParameters(), ConfigurationParameters.of(UserConstants.PARAM_AUTHORIZABLE_ACTION_PROVIDER, actionProvider)); } + + @Nullable + @Override + public PrincipalProvider getUserPrincipalProvider(@Nonnull Root root, @Nonnull NamePathMapper namePathMapper) { + return null; + } }; } else { return super.getConfiguration(configClass); Modified: jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user.md URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user.md?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user.md (original) +++ jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user.md Wed Aug 17 14:21:07 2016 @@ -18,14 +18,16 @@ User Management -------------------------------------------------------------------------------- -### JCR User Management +<a href="jcr_api"/> +### JCR API JCR itself doesn't come with a dedicated user management API. The only method related and ultimately used for user management tasks is `Session.getUserID()`. Therefore an API for user and group management has been defined as part of the extensions present with Jackrabbit API. -### Jackrabbit User Management API +<a name="jackrabbit_api"/> +### Jackrabbit API The Jackrabbit API provides the user management related extensions that are missing in JCR. The relevant interfaces are defined in the @@ -39,214 +41,26 @@ missing in JCR. The relevant interfaces - `QueryBuilder` - `Query` -### Oak User Management Implementation - -The default user management implementation stores user/group information in the -content repository. In contrast to Jackrabbit 2.x, which by default used a single, -dedicated workspace for user/group data, this data will as of Oak 1.0 be stored -separately for each JCR workspace. - -Consequently the `UserManager` associated with the editing sessions, performs -all actions with this editing session. This corresponds to the behavior as defined -the alternative implementation present with Jackrabbit 2.x ((see Jackrabbit 2.x `UserPerWorkspaceUserManager`). - -#### General -* The Oak implementation is build on the Oak API. This allows for double usage as - extension to the JCR API as well as within the Oak layer (aka SPI). -* The `UserManager` is always associated with the same JCR workspace as the editing - `Session` from which the class has been obtained. -* Changes made to the user management API are always transient and require `Session#save()` to be persisted. -* In case of any failure during user management related write operations the API - consumer is in charge of specifically revert pending or invalid transient modifications - or calling `Session#refresh(false)`. - -#### Differences wrt Jackrabbit 2.x - -A summary of all changes with respect to the former implementation present with -Jackrabbit 2.x is present in the corresponding [section](user/differences.html). - -#### Built-in Users and Special Groups - -The setup of builtin user and group accounts is triggered by the configured `WorkspaceInitializer` -associated with the user management configuration (see Configuration section below). - -The default user management implementation in OAK comes with an initializer that -creates the following builtin user accounts: - -##### Administrator -The admin user is always being created. The ID of this user is retrieved from the -user configuration parameter `PARAM_ADMIN_ID`, which defaults to `admin`. - -As of OAK 1.0 however the administrator user might be created without initial -password forcing the application to set the password upon start (see `PARAM_OMIT_ADMIN_PW` -configuration parameter). - -##### Anonymous User -In contrast to Jackrabbit 2.x the anonymous (or guest) user is optional. Creation -will be skipped if the value of the `PARAM_ANONYMOUS_ID` configuration parameter -is `null` or empty. - -Note, that the anonymous user will always be created without specifying a password -in order to prevent regular login with `SimpleCredentials`. -The proper way to obtain a guest session is: - - Repository#login(new GuestCredentials(), wspName); - -See section [Authentication](authentication.html) for further information about -guest login. - -##### Everyone Group - -The default user management implementation in Oak contains special handling for -the optional group that represents _everyone_, which is marked by the reserved -name [everyone] and corresponds to the `EveryonePrincipal`. - -This special group always contains all Authorizable as member and cannot be edited -with user management API. As of OAK this fact is consistently reflected in all -group membership related methods. See also [Principal Management](principal.html). - -#### Reading Authorizables - -##### Handling of the Authorizable ID -* As of Oak the node type definition of `rep:Authorizable` defines a new property `rep:authorizableId` which is intended to store the ID of a user or group. -* The default implementation comes with a dedicated property index for `rep:authorizableId` which asserts the uniqueness of that ID. -* `Authorizable#getID` returns the string value contained in `rep:authorizableID` and for backwards compatibility falls back on the node name in case the ID property is missing. -* The name of the authorizable node is generated based on a configurable implementation of the `AuthorizableNodeName` interface (see configuration section below). By default it uses the ID as name hint and includes a conversion to a valid JCR node name. - -##### equals() and hashCode() -The implementation of `Object#equals()` and `Object#hashCode()` for user and -groups slightly differs from Jackrabbit 2.x. It no longer relies on the _sameness_ -of the underlaying JCR node but only compares IDs and the user manager instance. - -#### Creating Authorizables -* The `rep:password` property is no longer defined to be mandatory. Therefore a new user might be created without specifying a password. Note however, that `User#changePassword` does not allow to remove the password property. -* `UserManager#createGroup(Principal)` will no longer generate a groupID in case the principal name collides with an existing user or group ID. This has been considered redundant as the Jackrabbit API in the mean time added `UserManager#createGroup(String groupID)`. -* Since OAK is designed to scale with flat hierarchies the former configuration options `autoExpandTree` and `autoExpandSize` are no longer supported. - -#### Query - -See section [Searching Users and Groups](user/query.html) for details. - -#### Group Membership - -See section [Group Membership](user/membership.html) for details. - -#### Autosave Behavior -Due to the nature of the UserManager (see above) we decided to drop the auto-save -behavior in the default implementation present with OAK. Consequently, - -* `UserManager#autoSave(boolean)` throws `UnsupportedRepositoryOperationException` -* `UserManager#isAutoSave()` always returns `false` - -See also `PARAM_SUPPORT_AUTOSAVE` below; while this should not be needed if -application code has been written against the Jackrabbit API (and thus testing if -auto-save mode is enabled or not) this configuration option can be used as last resort. - - -#### User/Group Representation in the Repository - -The following block lists the built-in node types related to user management tasks: - - [rep:Authorizable] > mix:referenceable, nt:hierarchyNode - abstract - + * (nt:base) = nt:unstructured VERSION - - rep:principalName (STRING) protected mandatory - - rep:authorizableId (STRING) protected /* @since oak 1.0 */ - - * (UNDEFINED) - - * (UNDEFINED) multiple - - [rep:Group] > rep:Authorizable, rep:MemberReferences - + rep:members (rep:Members) = rep:Members multiple protected VERSION /* @deprecated */ - + rep:membersList (rep:MemberReferencesList) = rep:MemberReferencesList protected COPY - - /** @since oak 1.0 */ - [rep:MemberReferences] - - rep:members (WEAKREFERENCE) protected multiple < 'rep:Authorizable' - - /** @since oak 1.0 */ - [rep:MemberReferencesList] - + * (rep:MemberReferences) = rep:MemberReferences protected COPY - - /** @deprecated since oak 1.0 */ - [rep:Members] - orderable - + * (rep:Members) = rep:Members protected multiple - - * (WEAKREFERENCE) protected < 'rep:Authorizable' - -<a name="validation"/> -##### Validation - -The consistency of this content structure is asserted by a dedicated `UserValidator`. -The corresponding errors are all of type `Constraint` with the following codes: - -| Code | Message | -|-------------------|----------------------------------------------------------| -| 0020 | Admin user cannot be disabled | -| 0021 | Invalid jcr:uuid for authorizable (creation) | -| 0022 | Changing Id, principal name after creation | -| 0023 | Invalid jcr:uuid for authorizable (mod) | -| 0024 | Password may not be plain text | -| 0025 | Attempt to remove id, principalname or pw | -| 0026 | Mandatory property rep:principalName missing | -| 0027 | The admin user cannot be removed | -| 0028 | Attempt to create outside of configured scope | -| 0029 | Intermediate folders not rep:AuthorizableFolder | -| 0030 | Missing uuid for group (check for cyclic membership) | -| 0031 | Cyclic group membership | -| 0032 | Attempt to set password with system user | -| 0033 | Attempt to add rep:pwd node to a system user | - - -#### XML Import -As of Oak 1.0 user and group nodes can be imported both with Session and Workspace -import. Other differences compared to Jackrabbit 2.x: - -* Importing an authorizable to another tree than the configured user/group node will only failed upon save (-> see `UserValidator` during the `Root#commit`). With Jackrabbit 2.x core it used to fail immediately. -* The `BestEffort` behavior is now also implemented for the import of impersonators (was missing in Jackrabbit /2.x). - +<a name="api_extensions"/> ### API Extensions + The Oak project introduces the following user management related public interfaces and classes: -#### Authorizable Actions - -The former internal Jackrabbit interface `AuthorizableAction` has been slightly -adjusted to match Oak requirements and is now part of the public Oak SPI interfaces. -In contrast to Jackrabbit-core the AuthorizableAction(s) now operate directly on -the Oak API, which eases the handling of implementation specific tasks such as -writing protected items. - -See section [Authorizable Actions](user/authorizableaction.html) for further -details and examples. - -#### Node Name Generation - -The default user management implementation with Oak 1.0 allows to specify how -the name of a new authorizable node is being generated. - -See section [Authorizable Node Name](user/authorizablenodename.html) for further -details and examples. - -#### Password Expiry and Force Initial Password Change +- `AuthorizableType`: ease handling with the different authorizable types. +- `AuthorizableAction` and `AuthorizableActionProvider`: see [Authorizable Actions](user/authorizableaction.html) for details. +- `AuthorizableNodeName`: see section [Authorizable Node Name Generation](user/authorizablenodename.html). +- `GroupAction` (via `AuthorizableActionProvider`): see [Group Actions](user/groupaction.html) for details. +- `UserAuthenticationFactory`: see sections [pluggability](user/default.html#pluggability) +and [user authentication](authentication/default.html#user_authentication) for additional details. -Since Oak 1.1.0 the default user management and authentication implementation -provides password expiry and initial password change. - -By default these features are disabled. The corresponding configuration options -are - -- `PARAM_PASSWORD_MAX_AGE`: number of days until the password expires. -- `PARAM_PASSWORD_INITIAL_CHANGE`: boolean flag to enable this feature. - -See section [Password Expiry and Force Initial Password Change](user/expiry.html) -for details. - -#### Utilities +<a href="utilities"/> +### Utilities `org.apache.jackrabbit.oak.spi.security.user.*` -- `AuthorizableType` : Ease handling with the different authorizable types. - `UserConstants` : Constants (NOTE: OAK names/paths) +- `UserIdCredentials` : Simple credentials implementation that might be used for `User.getCredentials' without exposing pw information. `org.apache.jackrabbit.oak.spi.security.user.util.*` @@ -256,74 +70,53 @@ for details. function for password generation. - `UserUtil` : Utilities related to general user management tasks. +<a href="default_implementation"/> +### Oak User Management Implementation + +The behavior of the default user management implementation is described in section +[User Management: The Default Implementation](user/default.html). +<a name="configuration"/> ### Configuration -The following user management specific methods are present with the [UserConfiguration] -as of OAK 1.0: +The Oak user management comes with a dedicated entry point called [UserConfiguration]. +This class is responsible for passing configuration options to the implementation +and provides the following two methods: -* getUserManager: Obtain a new user manager instance +- `getUserManager(Root, NamePathMapper)`: get a new `UserManager` instance +- `getUserPrincipalProvider(Root, NamePathMapper)`: optional method that allows for optimized principal look-up from user/group accounts (since Oak 1.3.4). -#### Configuration Parameters supported by the default implementation +#### Configuration Parameters -| Parameter | Type | Default | -|-------------------------------------|---------|----------------------------------------------| -| `PARAM_ADMIN_ID` | String | "admin" | -| `PARAM_OMIT_ADMIN_PW` | boolean | false | -| `PARAM_ANONYMOUS_ID` | String | "anonymous" (nullable) | -| `PARAM_USER_PATH` | String | "/rep:security/rep:authorizables/rep:users" | -| `PARAM_GROUP_PATH` | String | "/rep:security/rep:authorizables/rep:groups" | -| `PARAM_DEFAULT_DEPTH` | int | 2 | -| `PARAM_PASSWORD_HASH_ALGORITHM` | String | "SHA-256" | -| `PARAM_PASSWORD_HASH_ITERATIONS` | int | 1000 | -| `PARAM_PASSWORD_SALT_SIZE` | int | 8 | -| `PARAM_AUTHORIZABLE_NODE_NAME` | AuthorizableNodeName | AuthorizableNodeName#DEFAULT | -| `PARAM_AUTHORIZABLE_ACTION_PROVIDER`| AuthorizableActionProvider | DefaultAuthorizableActionProvider | -| `PARAM_SUPPORT_AUTOSAVE` | boolean | false | -| `PARAM_IMPORT_BEHAVIOR` | String ("abort", "ignore", "besteffort") | "ignore" | -| `PARAM_PASSWORD_MAX_AGE` | int | 0 | -| `PARAM_PASSWORD_INITIAL_CHANGE` | boolean | false | -| | | | - -The following configuration parameters present with the default implementation in Jackrabbit 2.x are no longer supported and will be ignored: - -* 'compatibleJR16' -* 'autoExpandTree' -* 'autoExpandSize' -* 'groupMembershipSplitSize' +The supported configuration options of the default implementation are described in the corresponding [section](user/default.html#configuration). +<a name="pluggability"/> ### Pluggability -The default security setup as present with Oak 1.0 is able to provide custom -implementation on various levels: +The default security setup as present with Oak 1.0 is able to have the default +user management implementation replaced as follows: -1. The complete user management implementation can be changed by plugging a different - `UserConfiguration` implementations. In OSGi-base setup this is achieved by making - the configuration a service. In a non-OSGi-base setup the custom configuration - must be exposed by the `SecurityProvider` implementation. -2. Within the default user management implementation the following parts can be - change/extended at runtime by providing corresponding OSGi services or passing - appropriate configuration parameters exposing the custom implementations: - - `AuthorizableActionProvider`: Defines the authorizable actions, see [Authorizable Actions](user/authorizableaction.html). - - `AuthorizableNodeName`: Defines the generation of the authorizable node names - in case the user management implementation stores user information in the repository. - See [Authorizable Node Name Generation](user/authorizablenodename.html). +The complete user management implementation can be changed by plugging a different +`UserConfiguration` implementations. In OSGi-base setup this is achieved by making +the configuration a service which must take precedence over the default. +In a non-OSGi-base setup the custom configuration must be exposed by the +`SecurityProvider` implementation. + +Alternatively the default user management implementation can be extended and +adjusted using various means. See the corresponding [section](user/default.html#pluggability) +for further details. +<a name="further_reading"/> ### Further Reading - [Differences wrt Jackrabbit 2.x](user/differences.html) -- [Group Membership](user/membership.html) -- [Authorizable Actions](user/authorizableaction.html) -- [Authorizable Node Name](user/authorizablenodename.html) +- [User Management : The Default Implementation](user/default.html) + - [Group Membership](user/membership.html) + - [Authorizable Actions](user/authorizableaction.html) + - [Authorizable Node Name](user/authorizablenodename.html) + - [Password Expiry and Force Initial Password Change](user/expiry.html) + - [Password History](user/history.html) - [Searching Users and Groups](user/query.html) -- [Password Expiry and Force Initial Password Change](user/expiry.html) <!-- hidden references --> -[everyone]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/principal/EveryonePrincipal.html#NAME [UserConfiguration]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/user/UserConfiguration.html -[OAK-118]: https://issues.apache.org/jira/browse/OAK-118 -[OAK-482]: https://issues.apache.org/jira/browse/OAK-482 -[OAK-793]: https://issues.apache.org/jira/browse/OAK-793 -[OAK-949]: https://issues.apache.org/jira/browse/OAK-949 -[OAK-1183]: https://issues.apache.org/jira/browse/OAK-1183 - Modified: jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user/membership.md URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user/membership.md?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user/membership.md (original) +++ jackrabbit/oak/branches/1.2/oak-doc/src/site/markdown/security/user/membership.md Wed Aug 17 14:21:07 2016 @@ -30,6 +30,8 @@ member relationship of users and groups: - `isMember(Authorizable boolean` - `addMember(Authorizable) boolean` - `removeMember(Authorizable) boolen` + - `addMembers(String...) Set<String>` + - `removeMembers(String...) Set<String>` - [org.apache.jackrabbit.api.security.user.Authorizable] - `declaredMemberOf() Iterator<Group>` @@ -139,6 +141,29 @@ internally processed using the normal us node structure after the import might not be the same as the one represented in the input. +#### Add and Remove Group Members by Id + +Since Oak 1.3.4 the default user management implementation also allows to modify +group membership by specifying the member id(s) (see [JCR-3880] and [OAK-3170]). +The following details are worth mentioning: + +- a `null` or empty String id will immediately fail the operation with `ConstraintViolationException`; changes already made will not be reverted, +- an attemt to make the same group member of itself will list that id in the return value but will not fail the operation, +- duplicate ids in the parameter list will be silently ignored, +- cyclic membership validation is postponed to the validator called upon `Root.commit` + and will only fail at that point; the cyclic membership then needs to be manually + resolved by the application, +- whether or not a non-existing (or not accessible) authorizable can be added or + removed depends on the configured `ImportBehavior`: + - ABORT: each id is resolved to the corresponding authorizable; if it doesn't + exist `ConstraintViolationException` is thrown immediately; changes already + made will not be reverted. + - BESTEFFORT: the specified ids are not resolved to the corresponding + authorizables and are silently added|removed to|from the set of members; + ids that were not successfully added|removed are listed in the return value. + - IGNORE: each id is resolved to the corresponding authorizable; if it doesn't + exist it will be returned as _failed_ in the return value. + ### Configuration Note that as of Oak 1.0 the implementation is responsible for defining the @@ -149,4 +174,6 @@ with Jackrabbit 2.x is not supported any <!-- hidden references --> [org.apache.jackrabbit.api.security.user.Group]: http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Group.java -[org.apache.jackrabbit.api.security.user.Authorizable]: http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java \ No newline at end of file +[org.apache.jackrabbit.api.security.user.Authorizable]: http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/Authorizable.java +[JCR-3880]: https://issues.apache.org/jira/browse/JCR-3880 +[OAK-3170]: https://issues.apache.org/jira/browse/OAK-3170 Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/GroupDelegator.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/GroupDelegator.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/GroupDelegator.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/GroupDelegator.java Wed Aug 17 14:21:07 2016 @@ -20,6 +20,7 @@ package org.apache.jackrabbit.oak.jcr.delegate; import java.util.Iterator; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.jcr.RepositoryException; @@ -134,6 +135,17 @@ final class GroupDelegator extends Autho } @Override + public Set<String> addMembers(@Nonnull final String... memberIds) throws RepositoryException { + return sessionDelegate.perform(new SessionOperation<Set<String>>("addMembers") { + @Nonnull + @Override + public Set<String> perform() throws RepositoryException { + return getDelegate().addMembers(memberIds); + } + }); + } + + @Override public boolean removeMember(final Authorizable authorizable) throws RepositoryException { return sessionDelegate.perform(new SessionOperation<Boolean>("removeMember") { @Nonnull @@ -143,4 +155,15 @@ final class GroupDelegator extends Autho } }); } + + @Override + public Set<String> removeMembers(@Nonnull final String... memberIds) throws RepositoryException { + return sessionDelegate.perform(new SessionOperation<Set<String>>("removeMembers") { + @Nonnull + @Override + public Set<String> perform() throws RepositoryException { + return getDelegate().removeMembers(memberIds); + } + }); + } } Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java Wed Aug 17 14:21:07 2016 @@ -69,6 +69,7 @@ import org.apache.jackrabbit.oak.jcr.ses import org.apache.jackrabbit.oak.jcr.xml.ImportHandler; import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials; import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions; +import org.apache.jackrabbit.util.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.ContentHandler; @@ -745,6 +746,11 @@ public class SessionImpl implements Jack //--------------------------------------------------< JackrabbitSession >--- @Override + public boolean hasPermission(String absPath, String... actions) throws RepositoryException { + return hasPermission(absPath, Text.implode(actions, ",")); + } + + @Override @Nonnull public PrincipalManager getPrincipalManager() throws RepositoryException { return sessionContext.getPrincipalManager(); Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/AbstractEvaluationTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/AbstractEvaluationTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/AbstractEvaluationTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authorization/AbstractEvaluationTest.java Wed Aug 17 14:21:07 2016 @@ -45,6 +45,7 @@ import org.apache.jackrabbit.api.securit import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; +import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal; import org.apache.jackrabbit.test.NotExecutableException; import org.apache.jackrabbit.test.api.security.AbstractAccessControlTest; import org.junit.After; Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/AbstractImportTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/AbstractImportTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/AbstractImportTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/AbstractImportTest.java Wed Aug 17 14:21:07 2016 @@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.jcr.se import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -48,7 +47,6 @@ import org.apache.jackrabbit.oak.spi.xml import org.apache.jackrabbit.test.NotExecutableException; import org.junit.After; import org.junit.Before; -import org.junit.Test; import static org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest.dispose; import static org.junit.Assert.assertFalse; @@ -155,8 +153,16 @@ public abstract class AbstractImportTest protected abstract String getTargetPath(); + protected Session getImportSession() { + return adminSession; + } + + protected UserManager getUserManager() throws RepositoryException { + return ((JackrabbitSession) getImportSession()).getUserManager(); + } + protected Node getTargetNode() throws RepositoryException { - return adminSession.getNode(getTargetPath()); + return getImportSession().getNode(getTargetPath()); } protected String getExistingUUID() throws RepositoryException { @@ -171,6 +177,10 @@ public abstract class AbstractImportTest } protected void doImport(String parentPath, String xml, int importUUIDBehavior) throws Exception { + doImport(getImportSession(), parentPath, xml, importUUIDBehavior); + } + + protected void doImport(Session importSession, String parentPath, String xml, int importUUIDBehavior) throws Exception { InputStream in; if (xml.charAt(0) == '<') { in = new ByteArrayInputStream(xml.getBytes()); @@ -182,7 +192,7 @@ public abstract class AbstractImportTest in = getClass().getResourceAsStream(xml); } try { - adminSession.importXML(parentPath, in, importUUIDBehavior); + importSession.importXML(parentPath, in, importUUIDBehavior); } finally { in.close(); } @@ -196,24 +206,4 @@ public abstract class AbstractImportTest assertFalse(potentialID.equals(session.getNode(member.getPath()).getIdentifier())); } } - - private String getTestXml() { - StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); - for (StackTraceElement element : stackTraceElements) { - try { - Class<?> clazz = Class.forName(element.getClassName()); - for (Method method : clazz.getMethods()){ - if(method.getName().equals(element.getMethodName())){ - if (method.getAnnotation(Test.class) != null) { - return clazz.getSimpleName() + "-" + method.getName() + ".xml"; - } - } - - } - } catch (Exception e) { - // oops do something here - } - } - throw new IllegalArgumentException("no import xml given."); - } } Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportAbortTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportAbortTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportAbortTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportAbortTest.java Wed Aug 17 14:21:07 2016 @@ -64,7 +64,7 @@ public class GroupImportAbortTest extend } catch (RepositoryException e) { // success as well } finally { - adminSession.refresh(false); + getImportSession().refresh(false); } } } Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportBestEffortTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportBestEffortTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportBestEffortTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportBestEffortTest.java Wed Aug 17 14:21:07 2016 @@ -21,11 +21,14 @@ import java.util.List; import java.util.UUID; import javax.jcr.Node; import javax.jcr.PropertyType; +import javax.jcr.Session; import javax.jcr.Value; +import javax.jcr.nodetype.ConstraintViolationException; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; import org.apache.jackrabbit.oak.spi.xml.ImportBehavior; import org.apache.jackrabbit.test.NotExecutableException; @@ -57,6 +60,7 @@ public class GroupImportBestEffortTest e invalid.add(UUID.randomUUID().toString()); // random uuid invalid.add(getExistingUUID()); // uuid of non-authorizable node + Session s = getImportSession(); for (String id : invalid) { String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<sv:node sv:name=\"gFolder\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" + @@ -70,11 +74,11 @@ public class GroupImportBestEffortTest e try { // BESTEFFORT behavior -> must import non-existing members. doImport(getTargetPath(), xml); - Authorizable a = userMgr.getAuthorizable("g1"); + Authorizable a = getUserManager().getAuthorizable("g1"); if (a.isGroup()) { // the rep:members property must contain the invalid value boolean found = false; - Node grNode = adminSession.getNode(a.getPath()); + Node grNode = s.getNode(a.getPath()); for (Value memberValue : grNode.getProperty(UserConstants.REP_MEMBERS).getValues()) { assertEquals(PropertyType.WEAKREFERENCE, memberValue.getType()); if (id.equals(memberValue.getString())) { @@ -85,12 +89,12 @@ public class GroupImportBestEffortTest e assertTrue("ImportBehavior.BESTEFFORT must import non-existing members.",found); // declared members must not list the invalid entry. - assertNotDeclaredMember((Group) a, id, adminSession); + assertNotDeclaredMember((Group) a, id, s); } else { fail("'g1' was not imported as Group."); } } finally { - adminSession.refresh(false); + s.refresh(false); } } } @@ -100,7 +104,7 @@ public class GroupImportBestEffortTest e String g1Id = "0120a4f9-196a-3f9e-b9f5-23f31f914da7"; String nonExistingId = "b2f5ff47-4366-31b6-a533-d8dc3614845d"; // groupId of 'g' group. - if (userMgr.getAuthorizable("g") != null) { + if (getUserManager().getAuthorizable("g") != null) { throw new NotExecutableException(); } String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + @@ -115,11 +119,11 @@ public class GroupImportBestEffortTest e // BESTEFFORT behavior -> must import non-existing members. doImport(getTargetPath(), xml); - Authorizable g1 = userMgr.getAuthorizable("g1"); + Authorizable g1 = getUserManager().getAuthorizable("g1"); if (g1.isGroup()) { // the rep:members property must contain the invalid value boolean found = false; - Node grNode = adminSession.getNode(g1.getPath()); + Node grNode = getImportSession().getNode(g1.getPath()); for (Value memberValue : grNode.getProperty(UserConstants.REP_MEMBERS).getValues()) { assertEquals(PropertyType.WEAKREFERENCE, memberValue.getType()); if (nonExistingId.equals(memberValue.getString())) { @@ -138,7 +142,7 @@ public class GroupImportBestEffortTest e String g1Id = "0120a4f9-196a-3f9e-b9f5-23f31f914da7"; String nonExistingId = "b2f5ff47-4366-31b6-a533-d8dc3614845d"; // groupId of 'g' group. - if (userMgr.getAuthorizable("g") != null) { + if (getUserManager().getAuthorizable("g") != null) { throw new NotExecutableException(); } @@ -168,18 +172,31 @@ public class GroupImportBestEffortTest e membership references. expected: - group is imported - - circular membership is ignored + - circular membership is ignored (OR fails upon save) - g is member of g1 - - g1 isn't member of g + - g1 isn't member of g (circular membership detected) OR saving changes fails */ doImport(getTargetPath() + "/gFolder", xml2); - Authorizable g = userMgr.getAuthorizable("g"); - assertNotNull(g); - if (g.isGroup()) { - assertNotDeclaredMember((Group) g, g1Id, adminSession); + Group g = getUserManager().getAuthorizable("g", Group.class); + Group g1 = getUserManager().getAuthorizable("g1", Group.class); + + assertNotNull("'g' was not imported as Group.", g); + assertNotNull("'g1' was not imported as Group.", g1); + + assertTrue(g1.isDeclaredMember(g)); + if (g.isDeclaredMember(g1)) { + // circular membership created during import + try { + getImportSession().save(); + fail("Circular membership must be detected upon save."); + } catch (ConstraintViolationException e) { + Throwable th = e.getCause(); + assertTrue(th instanceof CommitFailedException); + assertEquals(31, ((CommitFailedException) th).getCode()); + } } else { - fail("'g' was not imported as Group."); + assertNotDeclaredMember(g, g1Id, getImportSession()); } } @@ -199,10 +216,10 @@ public class GroupImportBestEffortTest e "</sv:node>"; doImport(getTargetPath(), xml); - User user = userMgr.createUser("angi", "pw"); - adminSession.save(); + User user = getUserManager().createUser("angi", "pw"); + getImportSession().save(); - Group g1 = (Group) userMgr.getAuthorizable("g1"); + Group g1 = (Group) getUserManager().getAuthorizable("g1"); assertTrue(g1.isMember(user)); } Modified: jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java?rev=1756639&r1=1756638&r2=1756639&view=diff ============================================================================== --- jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java (original) +++ jackrabbit/oak/branches/1.2/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImportIgnoreTest.java Wed Aug 17 14:21:07 2016 @@ -56,9 +56,9 @@ public class GroupImportIgnoreTest exten "</sv:node>"; doImport(getTargetPath(), xml); // no exception during import -> member must have been ignored though. - Authorizable a = userMgr.getAuthorizable("g1"); + Authorizable a = getUserManager().getAuthorizable("g1"); if (a.isGroup()) { - assertNotDeclaredMember((Group) a, invalidId, adminSession); + assertNotDeclaredMember((Group) a, invalidId, getImportSession()); } else { fail("'g1' was not imported as Group."); } @@ -84,14 +84,14 @@ public class GroupImportIgnoreTest exten // there should be no exception during import, // but invalid members must be ignored. doImport(getTargetPath(), xml); - Authorizable a = userMgr.getAuthorizable("g1"); + Authorizable a = getUserManager().getAuthorizable("g1"); if (a.isGroup()) { - assertNotDeclaredMember((Group) a, id, adminSession); + assertNotDeclaredMember((Group) a, id, getImportSession()); } else { fail("'g1' was not imported as Group."); } } finally { - adminSession.refresh(false); + getImportSession().refresh(false); } } }
