You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2015/06/18 16:30:17 UTC

svn commit: r1686235 [6/6] - in /jackrabbit/oak/trunk: ./ oak-doc/src/site/markdown/ oak-exercise/ oak-exercise/src/ oak-exercise/src/main/ oak-exercise/src/main/java/ oak-exercise/src/main/java/org/ oak-exercise/src/main/java/org/apache/ oak-exercise/...

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L3_UserVsPrincipalTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L3_UserVsPrincipalTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L3_UserVsPrincipalTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L3_UserVsPrincipalTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.UUID;
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.AccessControlManager;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+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.api.security.user.UserManager;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.apache.jackrabbit.oak.security.ExerciseUtility.TEST_USER_HINT;
+import static org.apache.jackrabbit.oak.security.ExerciseUtility.TEST_GROUP_HINT;
+import static org.apache.jackrabbit.oak.security.ExerciseUtility.TEST_PRINCIPAL_HINT;
+import static org.apache.jackrabbit.oak.security.ExerciseUtility.TEST_GROUP_PRINCIPAL_HINT;
+
+/**
+ * <pre>
+ * Module: User Management | Principal Management
+ * =============================================================================
+ *
+ * Title: User vs. Principal
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Understand the difference between {@link org.apache.jackrabbit.api.security.user.User}
+ * and {@link java.security.Principal}.
+ *
+ * Exercises:
+ *
+ * - {@link #testLookup()}
+ *   Test case illustrating lookup of principals and authorizables by principal
+ *   name and ID as well as authorizable lookup by principal.
+ *   Fix the test-case and making you familiar with the distiction between
+ *   the ID and the principal name in the first place.
+ *
+ * - {@link #testCreateUserWithGroupPrincipalName()}
+ *   This test attempts to create a new user with a principal that is already
+ *   used by a Group. Complete the test-case such that it passes.
+ *
+ * - {@link #testCreateWithReverse()}
+ *   Test case that attempts to create a user using the principal name of another
+ *   user as ID and vice versa. Complete|Fix the test and explain the expected
+ *   and the actually behavior.
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link #testLoginWithPrincipalName()}
+ *   Test case creating SimpleCredentials from principal name + password and create a
+ *   new ContentSession from these credentials. Fix the case and make the
+ *   appropriate assertions.
+ *
+ * - {@link #testAccessControlEntryWithId()}
+ *   Test case attempting to create a new access control entry for a principal
+ *   based from an authorizable ID. Fix the case and make the appropriate assertions.
+ *
+ *
+ * Related Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link org.apache.jackrabbit.oak.security.authentication.L2_AuthInfoTest}
+ *   For tests related to exposure of principal and ID upon successful login.
+ *
+ * - {@link L6_AuthorizableContentTest}
+ *   for tests related to the content structure of users/groups and how the
+ *   ID and the principal name are represented there.
+ *
+ * </pre>
+ */
+public class L3_UserVsPrincipalTest extends AbstractSecurityTest {
+
+    private String testId = ExerciseUtility.getTestId(TEST_USER_HINT);
+    private Principal testPrincipal = ExerciseUtility.getTestPrincipal(TEST_PRINCIPAL_HINT);
+
+    private String testGroupId = ExerciseUtility.getTestId(TEST_GROUP_HINT);
+    private Principal testGroupPrincipal = ExerciseUtility.getTestPrincipal(TEST_GROUP_PRINCIPAL_HINT);
+
+    private User testUser;
+    private Group testGroup;
+
+    private PrincipalManager principalManager;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        UserManager userMgr = getUserManager(root);
+        testUser = ExerciseUtility.createTestUser(userMgr);
+        testGroup = userMgr.createGroup(testGroupId, testGroupPrincipal, null);
+
+        testGroup.addMember(testUser);
+        root.commit();
+
+        principalManager = getPrincipalManager(root);
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            testUser.remove();
+            testGroup.remove();
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    @Test
+    public void testLookup() throws RepositoryException {
+        Map<String, Object[]> resultMap = ImmutableMap.of(
+                testId, new Object[]{null, null, null},
+                testPrincipal.getName(), new Object[]{null, null, null},
+                testGroupId, new Object[]{null, null},
+                testGroupPrincipal.getName(), new Object[]{null, null}
+        );
+
+        for (String key : resultMap.keySet()) {
+            Object[] result = resultMap.get(key);
+
+            // lookup principal by "ID"
+            Principal expectedP = (Principal) result[0];
+            Principal principal = principalManager.getPrincipal(key);
+            assertEquals(expectedP, principal);
+
+            // lookup authorizable by "principal"
+            Principal p = new PrincipalImpl(key);
+            Authorizable a = getUserManager(root).getAuthorizable(p);
+            if (a != null) {
+                assertEquals(expectedP, a.getPrincipal());
+            }
+
+            // lookup Authorizable by "ID"
+            Authorizable expectedA = (Authorizable) result[1];
+            a = getUserManager(root).getAuthorizable(key);
+            assertEquals(expectedA, a);
+
+        }
+    }
+
+    @Test
+    public void testCreateUserWithGroupPrincipalName() throws RepositoryException, CommitFailedException {
+        // EXERCISE: fix the test-case with the correct assertions and exception catching! And explain why...
+        User user2 = null;
+        try {
+            user2 = getUserManager(root).createUser(UUID.randomUUID().toString(), ExerciseUtility.TEST_PW, testGroupPrincipal, null);
+            root.commit();
+        } finally {
+            if (user2 != null) {
+                user2.remove();
+                root.commit();
+            }
+        }
+    }
+
+    @Test
+    public void testCreateWithReverse() throws RepositoryException, CommitFailedException {
+        // EXERCISE: fix the test-case with the correct assertions and exception catching!
+        // EXERCISE: if creating the user suceeds : verify if the testUser and user2 are equal. explain why!
+        User user2 = null;
+        try {
+            user2 = getUserManager(root).createUser(testPrincipal.getName(), ExerciseUtility.TEST_PW, new PrincipalImpl(testId), null);
+            root.commit();
+
+            Boolean expectedEquals = null; // EXERCISE
+            assertEquals(expectedEquals.booleanValue(), testUser.equals(user2));
+
+        } finally {
+            if (user2 != null) {
+                user2.remove();
+                root.commit();
+            }
+        }
+    }
+
+    @Test
+    public void testLoginWithPrincipalName() throws Exception {
+        // EXERCISE fix the test case and add proper verification
+        login(ExerciseUtility.getTestCredentials(testPrincipal.getName())).close();
+    }
+
+    @Test
+    public void testAccessControlEntryWithId() throws RepositoryException {
+        AccessControlManager acMgr = getAccessControlManager(root);
+
+        // EXERCISE fix the test case
+        String[] ids = new String[] {testId, testGroupId};
+        for (String id : ids) {
+            AccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, "/");
+            acl.addAccessControlEntry(new PrincipalImpl(id), AccessControlUtils.privilegesFromNames(acMgr, PrivilegeConstants.JCR_READ));
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L3_UserVsPrincipalTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L4_AuthorizableIdTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L4_AuthorizableIdTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L4_AuthorizableIdTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L4_AuthorizableIdTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.util.TreeUtil;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Authorizable ID
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Understand what the ID of an authorizable stands for, how it is reflected
+ * in the API. Furthermore this exercises aims to make you familiar with
+ * some implementation details wrt the ID.
+ *
+ * Exercises:
+ *
+ * - {@link #getById()}
+ *   Use this test again to become familiar with the authorizable lookup by ID
+ *   and the nature of the authorizable ID.
+ *
+ * - {@link #testIdConflict()}
+ *   This test illustrates how the user management implementation deals with
+ *   conflicting IDs upon user/group creation.
+ *   Fix the test without changing the params of the createUser/createGroup calls.
+ *
+ *
+ * Advanced Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link #testIdWithManualCreation()}
+ *   This exercise aims to create a new user manually using the Oak API (which
+ *   doesn't apply the checks for protected items).
+ *   Use this test to understand how authorizable uniqueness is enforced in the
+ *   repository by fixing the test case
+ *
+ *   Question: Can you list all properties with a uniqueness constraint enforced upon?
+ *   Question: Can you explain how this is enforced and what is the part that defines this uniqueness?
+ *
+ *
+ * Related Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link L5_UuidTest}
+ *
+ * </pre>
+ *
+ * @see org.apache.jackrabbit.api.security.user.UserManager
+ * @see org.apache.jackrabbit.api.security.user.Authorizable#getID()
+ */
+public class L4_AuthorizableIdTest extends AbstractSecurityTest {
+
+    private User testUser;
+    private Group testGroup;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        testUser = ExerciseUtility.createTestUser(getUserManager(root));
+        testGroup = ExerciseUtility.createTestGroup(getUserManager(root));
+        root.commit();
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            if (testUser != null) {
+                testUser.remove();
+            }
+            if (testGroup != null) {
+                testGroup.remove();
+            }
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    @Test
+    public void testGetByID() throws RepositoryException {
+        // EXERCISE fix the test-case
+        UserManager uMgr = getUserManager(root);
+        Group g = uMgr.getAuthorizable(testUser.getID(), Group.class);
+
+        assertEquals(g, uMgr.getAuthorizable(testGroup.getID()));
+        assertEquals(g, uMgr.getAuthorizable(testGroup.getPrincipal().getName()));
+    }
+
+    @Test
+    public void testIdConflict() throws RepositoryException, CommitFailedException {
+        // EXERCISE: fix this test without changing the ID-parameter of the 2 create-calls.
+
+        User conflictUser = getUserManager(root).createUser(testUser.getID(), null);
+        root.refresh();
+
+        Group conflictGroup = getUserManager(root).createGroup(testUser.getID());
+        root.refresh();
+    }
+
+    @Test
+    public void testIdWithManualCreation() throws RepositoryException, CommitFailedException {
+        Tree userTree = root.getTree(testUser.getPath());
+        Tree authorizableFolder = userTree.getParent();
+
+        // EXERCISE: fix the test
+        try {
+            String id = TreeUtil.getString(userTree, UserConstants.REP_AUTHORIZABLE_ID);
+            Tree anotherUser = authorizableFolder.addChild("nodeName");
+            anotherUser.setProperty(userTree.getProperty(JcrConstants.JCR_PRIMARYTYPE));
+            anotherUser.setProperty(userTree.getProperty(JcrConstants.JCR_UUID));
+            anotherUser.setProperty(UserConstants.REP_PRINCIPAL_NAME, TreeUtil.getString(userTree, UserConstants.REP_PRINCIPAL_NAME));
+            anotherUser.setProperty(UserConstants.REP_AUTHORIZABLE_ID, id);
+            root.commit();
+
+            User another = getUserManager(root).getAuthorizable(id, User.class);
+
+            assertEquals(id, another.getID());
+            assertFalse(another.equals(testUser));
+
+        } finally {
+            root.refresh();
+            Tree t = authorizableFolder.getChild("nodeName");
+            if (t.exists()) {
+                t.remove();
+                root.commit();
+            }
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L4_AuthorizableIdTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L5_UuidTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L5_UuidTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L5_UuidTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L5_UuidTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Authorizable Uuid
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Understand the implementation specific content structure for users and groups.
+ * This particular exercise aims to illustrate the role of jcr:uuid.
+ *
+ * Exercises:
+ *
+ * - Authorizable Node Type Definition:
+ *   Look at the node type definition of {@code rep:Authorizable} in {@code builtin_nodetypes.cnd}
+ *   and implementation and answer the following questions:
+ *
+ *   - Why does a group or user node have a jcr:uuid property?
+ *   - What are the constraints JCR mandates for jcr:uuid? Also recap what JCR
+ *     states wrt {@link javax.jcr.Item#isSame(javax.jcr.Item)}. What are the
+ *     implications for the authorizable implementation as it is today?
+ *   - How is the jcr:uuid set in this default implementation?
+ *   - What is the jcr:uuid use for in this default implementation?
+ *
+ * - {@link #testIdAndUuidAndIdentifier()}
+ *   Use the answers provided above to fix the test. The goal is that you learn
+ *   to understand the difference between the ID as exposed by the user management
+ *   API and the internal JCR specific node identifiers.
+ *
+ * - {@link #testUuidUponCreation()}
+ *   Based on the answers above you should be able to fix the test case.
+ *
+ * </pre>
+ */
+public class L5_UuidTest extends AbstractJCRTest {
+
+    private UserManager userManager;
+    private User testUser;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        userManager = ((JackrabbitSession) superuser).getUserManager();
+        testUser = ExerciseUtility.createTestUser(userManager);
+        superuser.save();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            testUser.remove();
+            superuser.save();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private Node getUserNode(User user) throws RepositoryException {
+        String userPath = user.getPath();
+        return superuser.getNode(userPath);
+    }
+
+    public void testIdAndUuidAndIdentifier() throws RepositoryException {
+        Node userNode = getUserNode(testUser);
+
+        assertTrue(userNode.isNodeType(JcrConstants.MIX_REFERENCEABLE));
+
+        String identifier = userNode.getIdentifier();
+        String uuid = userNode.getUUID();
+        String authorizableId = userNode.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString();
+
+        // EXERCISE: explain why identifier and uuid are expected to be the equal
+        assertEquals(identifier, uuid);
+
+        // EXERCISE: explain why neither uuid nor identifier are expected to be equal to the rep:authoriableId property
+        assertFalse(identifier.equals(authorizableId));
+        assertFalse(uuid.equals(authorizableId));
+
+        String userId = testUser.getID();
+        String expectedUserId = null; // EXERCISE: what is the expected userID ?
+
+        assertEquals(expectedUserId, userId);
+
+        // EXERCISE: what id do you have to use for the lookup on the user manager?
+        String idForLookup = null;
+        User user = userManager.getAuthorizable(idForLookup, User.class);
+
+        Authorizable expectedAuthorizable = null; // EXERCISE
+        assertEquals(expectedAuthorizable, userManager.getAuthorizable(uuid));
+    }
+
+    public void testUuidUponCreation() throws RepositoryException {
+        Node userNode = getUserNode(testUser);
+        String uuid = userNode.getUUID();
+
+        // remove the test user
+        testUser.remove();
+        superuser.save();
+
+        // recreate the same user again
+        testUser = userManager.createUser(testUser.getID(), ExerciseUtility.TEST_PW);
+
+        // EXERCISE: fill the expected identifier.
+        // Q: can you predict the expected identifier?
+        // Q: if yes, why?
+        String expectedUuid = null;
+        assertEquals(expectedUuid, getUserNode(testUser).getUUID());
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L5_UuidTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L6_AuthorizableContentTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L6_AuthorizableContentTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L6_AuthorizableContentTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L6_AuthorizableContentTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import java.util.List;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.api.JackrabbitSession;
+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.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Representation of User|Group Content in the Repository
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Understand how the default implementation stores user/group information.
+ * After having completed this test you should be familiar with the node
+ * type definitions for rep:Authorizable, rep:User and rep:Group and be
+ * able to map some basic API calls to the individual properties.
+ *
+ * Exercises:
+ *
+ * - Overview
+ *   Look {@code org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd}
+ *   and try to identify the built in node types used to store user and group
+ *   content.
+ *
+ *   Question: Can explain the meaning of all types?
+ *   Question: Why are most item definitions protected?
+ *   Question: Can you explain which child item definitions are not protected and why?
+ *
+ * - {@link #testUserNode()}
+ *   Test case for user nodes.
+ *
+ * - {@link #testUserNodeType()}
+ *   What is the primary type of a user node?
+ *   Which mixin types are present in addition?
+ *
+ * - {@link #testGroupNode()}
+ *   Test case for group nodes.
+ *
+ * - {@link #testGroupNodeType()}
+ *   What is the primary type of a group node?
+ *   Which mixin types are present in addition?
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - Discuss why {@link #testUserNode()} doesn't include the password?
+ * - Discuss why {@link #testGroupNode()} doesn't include the 'disabled' flag.
+ *
+ *
+ * Related Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link L5_UuidTest ()}
+ * - {@link L11_PasswordTest}
+ * - {@link L12_PasswordExpiryTest}
+ * - {@link L8_MembershipTest}
+ *
+ * </pre>
+ *
+ * @see org.apache.jackrabbit.api.security.user.User
+ * @see org.apache.jackrabbit.api.security.user.Group
+ * @see org.apache.jackrabbit.oak.spi.security.user.UserConstants
+ */
+public class L6_AuthorizableContentTest extends AbstractJCRTest {
+
+    private UserManager userManager;
+
+    private User testUser;
+    private Group testGroup;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        userManager = ((JackrabbitSession) superuser).getUserManager();
+        testUser = ExerciseUtility.createTestUser(userManager);
+        testUser.disable("no longer active");
+
+        testGroup = ExerciseUtility.createTestGroup(userManager);
+
+        superuser.save();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (testUser != null) {
+                testUser.remove();
+            }
+            if (testGroup != null) {
+                testGroup.remove();
+            }
+            superuser.save();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private Node getAuthorizableNode(Authorizable authorizable) throws RepositoryException {
+        String path = authorizable.getPath();
+        return superuser.getNode(path);
+    }
+
+    public void testUserNode() throws RepositoryException {
+        Node node = getAuthorizableNode(testUser);
+
+        String idPropertyName = null; // EXERCISE
+        assertEquals(testUser.getID(), node.getProperty(idPropertyName).getString());
+
+        String principalPropertyName = null; // EXERCISE
+        assertEquals(testUser.getPrincipal().getName(), node.getProperty(principalPropertyName).getString());
+
+        String disabledPropertyName = null; // EXERCISE
+        assertEquals(testUser.getDisabledReason(), node.getProperty(disabledPropertyName));
+    }
+
+    public void testUserNodeType() throws RepositoryException {
+        Node node = getAuthorizableNode(testUser);
+
+        String expectedNodeTypeName = null; // EXERCISE
+        assertEquals(expectedNodeTypeName, node.getPrimaryNodeType().getName());
+
+        List<String> mixinTypes = ImmutableList.of(); // EXERCISE : fill the list
+        for (String mixin : mixinTypes) {
+            assertTrue(node.isNodeType(mixin));
+        }
+    }
+
+    public void testGroupNode() throws RepositoryException {
+        Node node = getAuthorizableNode(testGroup);
+
+        String idPropertyName = null; // EXERCISE
+        assertEquals(testGroup.getID(), node.getProperty(idPropertyName).getString());
+
+        String principalPropertyName = null; // EXERCISE
+        assertEquals(testGroup.getPrincipal().getName(), node.getProperty(principalPropertyName).getString());
+
+        String expectedNodeTypeName = null; // EXERCISE
+        assertEquals(expectedNodeTypeName, node.getPrimaryNodeType().getName());
+    }
+
+    public void testGroupNodeType() throws RepositoryException {
+        Node node = getAuthorizableNode(testGroup);
+
+        String expectedNodeTypeName = null; // EXERCISE
+        assertEquals(expectedNodeTypeName, node.getPrimaryNodeType().getName());
+
+        List<String> mixinTypes = ImmutableList.of(); // EXERCISE : fill the list
+        for (String mixin : mixinTypes) {
+            assertTrue(node.isNodeType(mixin));
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L6_AuthorizableContentTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L7_AuthorizablePropertiesTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L7_AuthorizablePropertiesTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L7_AuthorizablePropertiesTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L7_AuthorizablePropertiesTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import java.util.Iterator;
+import java.util.List;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+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.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Authorizable Properties
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * The aim of this exercise is to be aware of the API calls to set arbitrary
+ * (non-protected) properties on authorizables.
+ *
+ * Exercises:
+ *
+ * - Overview
+ *   List all methods defined on {@link org.apache.jackrabbit.api.security.user.Authorizable}
+ *   that allow to read and write arbitrary properties on a given user or group.
+ *
+ * - {@link #testArbitraryProperties()}
+ *   Become familiar with the API to read arbitrary properties with an user
+ *   or group. Also fill in a list of properties defined by JCR and the
+ *   authorizable node types that cannot be obtained using the methods define
+ *   on {@link org.apache.jackrabbit.api.security.user.Authorizable}.
+ *
+ * - {@link #testSpecialProperties()}
+ *   This tests uses the user properties API to retrieve protected JCR and
+ *   user management internal properties.
+ *   Fix the tests according to your expectations and your understanding of
+ *   the API contract and the implementation.
+ * </pre>
+ *
+ * @see org.apache.jackrabbit.api.security.user.Authorizable
+ */
+public class L7_AuthorizablePropertiesTest extends AbstractJCRTest {
+
+    private final static String EMAIL_REL_PATH = "properties/email";
+    private final static String PETS_REL_PATH = "pets";
+
+    private UserManager userManager;
+
+    private User testUser;
+    private Group testGroup;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        userManager = ((JackrabbitSession) superuser).getUserManager();
+        testUser = ExerciseUtility.createTestUser(userManager);
+        testUser.disable("no longer active");
+
+        testGroup = ExerciseUtility.createTestGroup(userManager);
+        testGroup.addMember(testUser);
+        testGroup.setProperty("Name", superuser.getValueFactory().createValue("Test Group"));
+
+        superuser.save();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (testUser != null) {
+                testUser.remove();
+            }
+            if (testGroup != null) {
+                testGroup.remove();
+            }
+            superuser.save();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private Node getAuthorizableNode(Authorizable authorizable) throws RepositoryException {
+        String path = authorizable.getPath();
+        return superuser.getNode(path);
+    }
+
+    public void testArbitraryProperties() throws RepositoryException {
+        // set 2 different properties (single and multivalued)
+        testUser.setProperty(EMAIL_REL_PATH, superuser.getValueFactory().createValue("testUser@oak.apache.org"));
+        testUser.setProperty(PETS_REL_PATH, new Value[]{
+                superuser.getValueFactory().createValue("cat"), superuser.getValueFactory().createValue("rabbit")
+        });
+        superuser.save();
+
+
+        Node node = getAuthorizableNode(testUser);
+
+        // EXERCISE: build the list of existing user properties rel paths
+        List<String> userPropertiesPath = ImmutableList.of();
+        for (String relPath : userPropertiesPath) {
+            assertTrue(testUser.hasProperty(relPath));
+        }
+
+        Value[] emailsExpected = testUser.getProperty(EMAIL_REL_PATH);
+        String expectedRelPath = null; // EXERCISE
+        assertEquals(emailsExpected[0], node.getProperty(expectedRelPath).getValue());
+
+        Value[] petsExpected = testUser.getProperty(PETS_REL_PATH);
+        expectedRelPath = null; // EXERCISE
+        assertArrayEquals(petsExpected, node.getProperty(expectedRelPath).getValues());
+
+        // EXERCISE: build a list of protected JCR properties that cannot be accessed using the Authorizable interface
+        List<String> protectedJcrPropertyNames = ImmutableList.of();
+        for (String relPath : protectedJcrPropertyNames) {
+            assertFalse(testUser.hasProperty(relPath));
+        }
+
+        // EXERCISE: build a list of protected properties defined by the authorizable or user node type definitions that cannot be accessed using the Authorizable interface
+        List<String> protectedAuthorizablePropertyNames = ImmutableList.of();
+        for (String relPath : protectedAuthorizablePropertyNames) {
+            assertFalse(testUser.hasProperty(relPath));
+        }
+
+        // remove the properties again
+        testUser.removeProperty(EMAIL_REL_PATH);
+        testUser.removeProperty(PETS_REL_PATH);
+        superuser.save();
+    }
+
+    public void testSpecialProperties() throws RepositoryException {
+        List<String> expectedGroupPropNames = null; // EXERCISE
+        Iterator<String> propNames = testGroup.getPropertyNames();
+
+        while (propNames.hasNext()) {
+            assertTrue(expectedGroupPropNames.remove(propNames.next()));
+        }
+        assertTrue(expectedGroupPropNames.isEmpty());
+
+        Boolean hasPrimaryType = null; // EXERCISE
+        assertEquals(hasPrimaryType.booleanValue(), testGroup.hasProperty(JcrConstants.JCR_PRIMARYTYPE));
+
+        Value[] expectedMembers = null; // EXERCISE
+        Value[] members = testGroup.getProperty(UserConstants.REP_MEMBERS);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L7_AuthorizablePropertiesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L8_MembershipTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L8_MembershipTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L8_MembershipTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L8_MembershipTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.jcr.RepositoryException;
+
+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.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.util.TreeUtil;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Group Membership
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * After having completed this exercise you should be able to manage group
+ * membership relations using the Jackrabbit User Management API and understand
+ * the basic design on how group membership is store and handled in the
+ * Oak repository.
+ *
+ * Exercises:
+ *
+ * - Overview
+ *   Take a look at the Jackrabbit User Management API and how group membership
+ *   is being edited (in {@link org.apache.jackrabbit.api.security.user.Group}
+ *   and discovered in both directions on {@link org.apache.jackrabbit.api.security.user.Group}
+ *   and {@link org.apache.jackrabbit.api.security.user.Authorizable}.
+ *
+ * - {@link #testAddRemoveMembers()}
+ *   Add (and remove) members to the predefined groups such that the test passes.
+ *
+ * - {@link #testDeclaredMembership()}
+ *   This test illustrates how declared members are retrieved from a group and
+ *   how to obtain the declared group membership of a given authorizable (user or
+ *   group).
+ *
+ *   Question: This test is executed with full permission. Can you elaborate
+ *   what happens if the editing session has limited read access on the user/group
+ *   tree(s)?
+ *
+ * - {@link #testInheritedMembership()}
+ *   This test illustrates how all members of a group are retrieved and
+ *   how to obtain full group membership of a given authorizable (user or
+ *   group). Complete the test such that it passes.
+ *
+ * - {@link #testMembersContentStructure()}
+ *   In order to complete this exercise look at the built-in node types and
+ *   identify those types that deal with group membership.
+ *   Once you are familiar with the node type definitions look at
+ *   > {@link org.apache.jackrabbit.oak.security.user.MembershipProvider} and
+ *   > {@link org.apache.jackrabbit.oak.security.user.MembershipWriter}
+ *   and how they deal with massive amount of members on a given group.
+ *   Finally fix the test case :-)
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * As you can see from the API calls present as of Jackrabbit API 2.10 you
+ * currently need to have an existing authorizable at hand in order to add
+ * (or remove) it as member of a given group.
+ * Since group membership is stored as {@link javax.jcr.PropertyType#WEAKREFERENCE}
+ * the repository is not obligated to enforce the validity of the references
+ * and thus might choose to create membership references that cannot (yet) be
+ * resolved.
+ *
+ * Look at the following unit tests present with the oak-jcr module and observe
+ * how the configured {@link org.apache.jackrabbit.oak.spi.xml.ImportBehavior}
+ * affects the import of group-membership information that cannot be resolved
+ * to an existing authorizable:
+ *
+ * - {@link org.apache.jackrabbit.oak.jcr.security.user.GroupImportBestEffortTest#testImportNonExistingMemberBestEffort()}
+ * - {@link org.apache.jackrabbit.oak.jcr.security.user.GroupImportAbortTest#testImportNonExistingMemberAbort()}
+ * - {@link org.apache.jackrabbit.oak.jcr.security.user.GroupImportIgnoreTest#testImportNonExistingMemberIgnore()} ()}
+ *
+ * Exercises
+ *
+ * 1. Walk through the XML import and take a close look at
+ *    {@link org.apache.jackrabbit.oak.security.user.UserImporter}, where the XML
+ *    of the protected items defined with the various authorizable node types
+ *    are being handled. Compare the differences wrt to the import behavior.
+ *
+ * 2. Discuss possible use-cases where creating a group with members that don't
+ *    (yet) exist (anymore) might be helpful (or even required).
+ *
+ *
+ * Advanced Exercise:
+ * -----------------------------------------------------------------------------
+ *
+ * Having completed the additional exercises wrt {@link org.apache.jackrabbit.oak.spi.xml.ImportBehavior}
+ * and the XML import of non-existing group members, you may want to complete
+ * following exercise.
+ *
+ * Question: How might the implementation of the API extensions proposed in
+ * <a href="https://issues.apache.org/jira/browse/JCR-3880">JCR-3880</a> could
+ * look like such that the implementation both matches the API contract and
+ * is consistent with the XML import?
+ *
+ * </pre>
+ *
+ * @see org.apache.jackrabbit.api.security.user.Authorizable#declaredMemberOf()
+ * @see org.apache.jackrabbit.api.security.user.Authorizable#memberOf()
+ * @see org.apache.jackrabbit.api.security.user.Group#isMember(org.apache.jackrabbit.api.security.user.Authorizable)
+ * @see org.apache.jackrabbit.api.security.user.Group#isDeclaredMember(org.apache.jackrabbit.api.security.user.Authorizable)
+ * @see org.apache.jackrabbit.api.security.user.Group#getMembers()
+ * @see org.apache.jackrabbit.api.security.user.Group#getDeclaredMembers()
+ * @see org.apache.jackrabbit.api.security.user.Group#addMember(org.apache.jackrabbit.api.security.user.Authorizable)
+ * @see org.apache.jackrabbit.api.security.user.Group#removeMember(org.apache.jackrabbit.api.security.user.Authorizable)
+ */
+public class L8_MembershipTest extends AbstractSecurityTest {
+
+    private User user;
+    private Group group;
+    private Group group2;
+    private Group group3;
+
+    private List<Authorizable> toRemove = new ArrayList<Authorizable>();
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        user = (User) createNewAuthorizable(false);
+        group = (Group) createNewAuthorizable(true);
+        group2 = (Group) createNewAuthorizable(true);
+        group3 = (Group) createNewAuthorizable(true);
+
+        root.commit();
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            for (Authorizable a : toRemove) {
+                a.remove();
+            }
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    private Authorizable createNewAuthorizable(boolean isGroup) throws RepositoryException {
+        UserManager uMgr = getUserManager(root);
+        Authorizable a = (isGroup) ? ExerciseUtility.createTestGroup(uMgr) : ExerciseUtility.createTestUser(uMgr);
+        toRemove.add(a);
+        return a;
+    }
+
+    @Test
+    public void testAddRemoveMembers() throws RepositoryException, CommitFailedException {
+        // EXERCISE: add group members such that the following test-cases passes.
+        root.commit();
+
+        assertTrue(group.isDeclaredMember(user));
+        assertFalse(group2.isDeclaredMember(user));
+        assertFalse(group3.isDeclaredMember(user));
+        assertTrue(group2.isMember(user));
+        assertTrue(group3.isMember(user));
+
+        Boolean isDeclaredMember = null; // EXERCISE
+        assertEquals(isDeclaredMember, group.isDeclaredMember(group2));
+
+        isDeclaredMember = null; // EXERCISE
+        assertEquals(isDeclaredMember, group3.isDeclaredMember(group));
+
+        Boolean isMember = null; // EXERCISE
+        assertEquals(isMember, group.isMember(group2));
+
+        isMember = null; // EXERCISE
+        assertEquals(isMember, group3.isMember(group));
+
+        // EXERCISE: now change the group membership such that the following assertions pass
+        root.commit();
+
+        assertFalse(group.isMember(user));
+        assertTrue(group.isMember(group2));
+        assertTrue(group3.isMember(group2));
+        assertTrue(group3.isMember(group));
+        assertTrue(group3.isMember(user));
+    }
+
+    @Test
+    public void testDeclaredMembership() throws RepositoryException, CommitFailedException {
+        group.addMember(group2);
+        group2.addMember(group3);
+        group3.addMember(user);
+        root.commit();
+
+        Set<Group> expectedGroups = null; // EXERCISE
+        Iterator<Group> groups = user.declaredMemberOf();
+        while (groups.hasNext()) {
+            assertTrue(expectedGroups.remove(groups.next()));
+        }
+        assertTrue(expectedGroups.isEmpty());
+
+        Set<Authorizable> expectedMembers = null; // EXERCISE
+        Iterator<Authorizable> memberIterator = group.getDeclaredMembers();
+        while (memberIterator.hasNext()) {
+            assertTrue(expectedMembers.remove(memberIterator.next()));
+        }
+        assertTrue(expectedMembers.isEmpty());
+    }
+
+    @Test
+    public void testInheritedMembership() throws RepositoryException, CommitFailedException {
+        group.addMember(group2);
+        group.addMember(group3);
+        group3.addMember(user);
+        root.commit();
+
+        Set<Group> groups = null; // EXERCISE
+        Iterator<Group> groupIterator = user.memberOf();
+        while (groupIterator.hasNext()) {
+            Group gr = groupIterator.next();
+            assertTrue(groups.remove(gr));
+        }
+        assertTrue(groups.isEmpty());
+
+        Set<Authorizable> expectedMembers = null; // EXERCISE
+        Iterator<Authorizable> memberIterator = group.getMembers();
+        while (memberIterator.hasNext()) {
+            assertTrue(expectedMembers.remove(memberIterator.next()));
+        }
+        assertTrue(expectedMembers.isEmpty());
+    }
+
+    @Test
+    public void testMembersContentStructure() throws RepositoryException, CommitFailedException {
+        int size = new MembershipWriter().getMembershipSizeThreshold() * 5;
+
+        List<String> memberUuids = new ArrayList<String>();
+        for (int i = 0; i < size; i++) {
+            Authorizable user = createNewAuthorizable(false);
+            String uuid = TreeUtil.getString(root.getTree(user.getPath()), JcrConstants.JCR_UUID);
+            //assertNotNull(uuid);
+            memberUuids.add(uuid);
+            group.addMember(user);
+        }
+        root.commit();
+
+        Tree groupTree = root.getTree(group.getPath());
+        Iterator<String> values = groupTree.getProperty(UserConstants.REP_MEMBERS).getValue(Type.STRINGS).iterator();
+        while (values.hasNext()) {
+            assertTrue(memberUuids.remove(values.next()));
+        }
+        assertFalse(memberUuids.isEmpty());
+
+        // EXERCISE: retrieve the rest of the member-information stored with the group
+        // EXERCISE: by looking at the tree structure created by the MembershipWriter
+
+        assertTrue(memberUuids.isEmpty());
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L8_MembershipTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L9_RemoveAuthorizableTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L9_RemoveAuthorizableTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L9_RemoveAuthorizableTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L9_RemoveAuthorizableTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+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.plugins.lock.LockConstants;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Remove Authorizables
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Understand why we strongly recommend not to remove (and recycle) user/group
+ * accounts.
+ *
+ * Exercises:
+ *
+ * - {@link #testAccessControlEntry()}
+ *   Test case illustrating the effect of removing the principal (for simplicity
+ *   represented by a test-user) referenced in an access control entry.
+ *   Explain the expected behavior and fix the test if necessary
+ *
+ * - {@link #testCreatedBy()}
+ *   Test case illustrating the effect of removing a user which created node
+ *   that is of type 'mix:created'.
+ *   Explain the expected behavior and fix the test if necessary
+ *
+ * - {@link #testLastModifiedBy()}
+ *   Test case illustrating the effect of removing a user which added the mixin
+ *   'mix:lastModified' to the test node.
+ *   Explain the expected behavior and fix the test if necessary
+ *
+ * - {@link #testLock()}
+ *   Test case illustrating the effect of removing a user which created a
+ *   open-scoped lock.
+ *
+ * - Based on the experiences from perfoming the above tests, summarize the effect
+ *   of removing an existing user and potentially re-using the same ID
+ *   at a later point.
+ *
+ *   Question: What are the implications from a security point of view
+ *   Question: What are possible consequences from a legal point of view
+ *
+ * - Use the user management API to identify alterntive ways such that you don't
+ *   need to remove the user.
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * In a OSGI-based Oak installation (Sling|Granite|CQ) you can extend this exercise:
+ *
+ * - Look for additional node types that store references to user or principals.
+ *   List node types and the properties
+ *
+ * - Inspect application code: can you find additional references to user/principals
+ *   stored?
+ *   Provide a list and discuss the impact from a security|legel point of view
+ *
+ * - Inspect the various log files for user or principal references
+ *   Discuss the legal implications of re-using them for different entities (subjects).
+ *
+ *
+ * Related Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link L10_RemovalAndMembershipTest ()}
+ *
+ * </pre>
+ *
+ */
+public class L9_RemoveAuthorizableTest extends AbstractJCRTest {
+
+    private UserManager userManager;
+
+    private User testUser;
+    private Session testSession;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        userManager = ((JackrabbitSession) superuser).getUserManager();
+        testUser = ExerciseUtility.createTestUser(userManager);
+
+        superuser.save();
+
+        // setup full access for test-user on the test-node
+        Privilege[] privileges = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_ALL);
+        if (!AccessControlUtils.addAccessControlEntry(superuser, testRoot, testUser.getPrincipal(), privileges, true)) {
+            throw new NotExecutableException();
+        }
+        superuser.save();
+
+        testSession = getHelper().getRepository().login(ExerciseUtility.getTestCredentials(testUser.getID()));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (testSession != null) {
+                testSession.logout();
+            }
+            if (testUser != null) {
+                testUser.remove();
+            }
+            superuser.save();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private void removeTestUser() throws RepositoryException {
+        testUser.remove();
+        superuser.save();
+        testUser = null;
+    }
+
+    private Node getAuthorizableNode(Authorizable authorizable) throws RepositoryException {
+        String path = authorizable.getPath();
+        return superuser.getNode(path);
+    }
+
+    public void testAccessControlEntry() throws RepositoryException, NotExecutableException {
+        // remove test user
+        removeTestUser();
+
+        boolean found = false;
+        AccessControlList acl = AccessControlUtils.getAccessControlList(superuser, testRoot);
+        if (acl != null) {
+            for (AccessControlEntry ace : acl.getAccessControlEntries()) {
+                if (testUser.getPrincipal().getName().equals(ace.getPrincipal().getName())) {
+                    found = true;
+                }
+            }
+        }
+
+        // EXERCISE: do you expect the ACE for test-principal to be still present? explain why and fix the test if necessary.
+        assertTrue(found);
+    }
+
+    public void testCreatedBy() throws RepositoryException {
+        Node testNode = testSession.getNode(testRoot);
+        Node folder = testNode.addNode("folder", JcrConstants.NT_FOLDER);
+        testSession.save();
+
+        // EXERCISE: explain why the folder node must have a jcr:created property.
+        assertTrue(folder.hasProperty(NodeTypeConstants.JCR_CREATEDBY));
+        assertEquals(testSession.getUserID(), folder.getProperty(NodeTypeConstants.JCR_CREATEDBY).getString());
+
+        removeTestUser();
+        testSession.refresh(false);
+
+        // EXERCISE: do you expect jcr:createdBy property to be still present? explain why and fix the test if necessary.
+        assertTrue(folder.hasProperty(NodeTypeConstants.JCR_CREATEDBY));
+    }
+
+    public void testLastModifiedBy() throws RepositoryException {
+        Node testNode = testSession.getNode(testRoot);
+        testNode.addMixin(NodeTypeConstants.MIX_LASTMODIFIED);
+        testNode.setProperty(propertyName1, "any value");
+        testSession.save();
+
+        assertTrue(testNode.hasProperty(NodeTypeConstants.JCR_LASTMODIFIEDBY));
+        assertEquals(testSession.getUserID(), testNode.getProperty(NodeTypeConstants.JCR_LASTMODIFIEDBY).getString());
+
+        removeTestUser();
+        testSession.refresh(false);
+
+        // EXERCISE: do you expect the property to be still present? explain why and fix the test if necessary.
+        assertTrue(testNode.hasProperty(NodeTypeConstants.JCR_LASTMODIFIEDBY));
+    }
+
+    public void testLock() throws RepositoryException {
+        Node testNode = testSession.getNode(testRoot);
+        testNode.addMixin(JcrConstants.MIX_LOCKABLE);
+        testSession.save();
+
+        testNode.lock(true, false);
+
+        try {
+            assertTrue(testNode.hasProperty(LockConstants.JCR_LOCKOWNER));
+            assertEquals(testSession.getUserID(), testNode.getProperty(LockConstants.JCR_LOCKOWNER).getString());
+
+            removeTestUser();
+            testSession.refresh(false);
+
+            // EXERCISE: do you expect the property to be still present? explain why and fix the test if necessary.
+            assertTrue(testNode.hasProperty(LockConstants.JCR_LOCKOWNER));
+
+        } finally {
+            testNode.unlock();
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/L9_RemoveAuthorizableTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L1_IntroductionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L1_IntroductionTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L1_IntroductionTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L1_IntroductionTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user.action;
+
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Introduction to Authorizable Actions
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * TODO
+ *
+ * Exercises:
+ *
+ * - {@link #TODO}
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * TODO
+ *
+ * </pre>
+ *
+ * @see TODO
+ */
+public class L1_IntroductionTest extends AbstractSecurityTest {
+
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L1_IntroductionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L2_AuthorizableActionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L2_AuthorizableActionTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L2_AuthorizableActionTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L2_AuthorizableActionTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.user.action;
+
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+
+/**
+ * <pre>
+ * Module: User Management
+ * =============================================================================
+ *
+ * Title: Authorizable Action in Detail
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * TODO
+ *
+ * Exercises:
+ *
+ * - {@link #TODO}
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * TODO
+ *
+ * </pre>
+ *
+ * @see TODO
+ */
+public class L2_AuthorizableActionTest extends AbstractSecurityTest {
+
+    // TODO
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/user/action/L2_AuthorizableActionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/pom.xml?rev=1686235&r1=1686234&r2=1686235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/pom.xml (original)
+++ jackrabbit/oak/trunk/pom.xml Thu Jun 18 14:30:16 2015
@@ -55,6 +55,7 @@
     <module>oak-pojosr</module>
     <module>oak-authorization-cug</module>
     <module>oak-remote</module>
+    <module>oak-exercise</module>
   </modules>
 
   <scm>