/*
 * 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.core.security.user;

import org.apache.jackrabbit.api.security.user.AbstractUserTest;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
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.core.SessionImpl;
import org.apache.jackrabbit.core.security.TestPrincipal;
import org.apache.jackrabbit.core.security.principal.EveryonePrincipal;
import org.apache.jackrabbit.core.security.user.action.AccessControlAction;
import org.apache.jackrabbit.core.security.user.action.AuthorizableAction;
import org.apache.jackrabbit.core.security.user.action.ClearMembershipAction;
import org.apache.jackrabbit.test.NotExecutableException;
import org.apache.jackrabbit.spi.commons.conversion.NameResolver;

import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.Node;
import javax.jcr.security.AccessControlList;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.AccessControlPolicyIterator;
import java.security.Principal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;

/**
 * <code>UserManagerImplTest</code>...
 */
public class UserManagerImplTest extends AbstractUserTest {

    private String pPrincipalName;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        if (!(userMgr instanceof UserManagerImpl)) {
            throw new NotExecutableException("UserManagerImpl expected -> cannot perform test.");
        }
        NameResolver resolver = (SessionImpl) superuser;
        pPrincipalName = resolver.getJCRName(UserConstants.P_PRINCIPAL_NAME);
    }

    private String getTestUserId(Principal p) throws RepositoryException {
        String hint = "UID" + p.getName();
        String userId = hint;
        int i = 0;
        while (userMgr.getAuthorizable(userId) != null) {
            userId = hint + i++;
        }
        return userId;
    }

    public void testPrincipalNameEqualsUserID() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        User u = null;
        try {
            u = userMgr.createUser(p.getName(), buildPassword(p));
            save(superuser);

            String msg = "Implementation specific: User.getID() must return the userID pass to createUser.";
            assertEquals(msg, u.getID(), p.getName());
        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testUserIDFromSession() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        User u = null;
        Session uSession = null;
        try {
            String uid = p.getName();
            String pw = buildPassword(p);
            u = userMgr.createUser(uid, pw);
            save(superuser);

            uSession = superuser.getRepository().login(new SimpleCredentials(uid, pw.toCharArray()));
            assertEquals(u.getID(), uSession.getUserID());
        } finally {
            if (uSession != null) {
                uSession.logout();
            }
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testCreateUserIdDifferentFromPrincipalName() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);
        String pw = buildPassword(uid);

        User u = null;
        Session uSession = null;
        try {
            u = userMgr.createUser(uid, pw, p, null);
            save(superuser);

            String msg = "Creating a User with principal-name distinct from Principal-name must succeed as long as both are unique.";
            assertEquals(msg, u.getID(), uid);
            assertEquals(msg, p.getName(), u.getPrincipal().getName());
            assertFalse(msg, u.getID().equals(u.getPrincipal().getName()));

            // make sure the userID exposed by a Session corresponding to that
            // user is equal to the users ID.
            uSession = superuser.getRepository().login(new SimpleCredentials(uid, pw.toCharArray()));
            assertEquals(uid, uSession.getUserID());
        } finally {
            if (uSession != null) {
                uSession.logout();
            }
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithInvalidIdOrPrincipal() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = p.getName();

        Map<String, Principal> fail = new HashMap<String, Principal>();
        fail.put(uid, null);
        fail.put(uid, new TestPrincipal(""));
        fail.put(null, p);
        fail.put("", p);      

        for (String id : fail.keySet()) {
            Group g = null;
            try {
                Principal princ = fail.get(id);
                g = userMgr.createGroup(id, princ, null);
                fail("Creating group with id '"+id+"' and principal '"+princ+"' should fail");
            } catch (IllegalArgumentException e) {
                // success
            } finally {
                if (g != null) {
                    g.remove();
                    save(superuser);
                }
            }
        }
    }

    public void testCreateEveryoneUser() throws Exception {
        User u = null;
        try {
            u = userMgr.createUser(EveryonePrincipal.NAME, "pw");
            fail("everyone is a reserved group name");
        } catch (IllegalArgumentException e) {
            // success
        } finally {
            if (u != null) {
                u.remove();
            }
        }

    }

    public void testCreateGroupWithId() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = p.getName();

        Group gr = null;
        try {

        	// assert group creation with exact ID
            gr = userMgr.createGroup(uid);
            save(superuser);
            assertEquals("Expect group with exact ID", uid, gr.getID());

        } finally {
            if (gr != null) {
                gr.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithIdAndPrincipal() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = p.getName();

        Group gr = null;
        try {

        	// assert group creation with exact ID
            gr = userMgr.createGroup(uid, p, null);
            save(superuser);

            assertEquals("Expect group with exact ID", uid, gr.getID());
            assertEquals("Expected group with exact principal name", p.getName(), gr.getPrincipal().getName());

        } finally {
            if (gr != null) {
                gr.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupIdDifferentFromPrincipalName() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        assertFalse(uid.equals(p.getName()));
        
        Group g = null;
        try {
            g = userMgr.createGroup(uid, p, null);
            save(superuser);

            String msg = "Creating a Group with principal-name distinct from Principal-name must succeed as long as both are unique.";
            assertEquals(msg, g.getID(), uid);
            assertEquals(msg, p.getName(), g.getPrincipal().getName());
            assertFalse(msg, g.getID().equals(g.getPrincipal().getName()));

        } finally {
            if (g != null) {
                g.remove();
                save(superuser);
            }
        }
    }

    public void testCreatingGroupWithPrincipalMatchingExistingUserId() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        User u = null;
        Group gr = null;
        try {
            u = userMgr.createUser(uid, buildPassword(uid), p, null);
            save(superuser);
            gr = userMgr.createGroup(new TestPrincipal(uid));
            save(superuser);

            String msg = "Creating a Group with a principal-name that exists as UserID -> must create new GroupID but keep PrincipalName.";
            assertFalse(msg, gr.getID().equals(gr.getPrincipal().getName()));
            assertFalse(msg, gr.getID().equals(uid));
            assertFalse(msg, gr.getID().equals(u.getID()));
            assertEquals(msg, uid, gr.getPrincipal().getName());
        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
            if (gr != null) {
                gr.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithExistingPrincipal() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = p.getName();

        User u = null;
        try {
        	// create a user with the given ID
            u = userMgr.createUser(uid, buildPassword(uid), p, null);
            save(superuser);

            // assert AuthorizableExistsException for principal that is already in use
            Group gr = null;
            try {
            	gr = userMgr.createGroup(p);
            	fail("Principal " + p.getName() + " is already in use -> must throw AuthorizableExistsException.");
            } catch (AuthorizableExistsException aee) {
            	// expected this
            } finally {
                if (gr != null) {
                    gr.remove();
                    save(superuser);
                }
            }

        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithExistingPrincipal2() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        assertFalse(uid.equals(p.getName()));

        User u = null;
        try {
        	// create a user with the given ID
            u = userMgr.createUser(uid, buildPassword(uid), p, null);
            save(superuser);

            // assert AuthorizableExistsException for principal that is already in use
            Group gr = null;
            try {
            	gr = userMgr.createGroup(p);
            	fail("Principal " + p.getName() + " is already in use -> must throw AuthorizableExistsException.");
            } catch (AuthorizableExistsException aee) {
            	// expected this
            } finally {
                if (gr != null) {
                    gr.remove();
                    save(superuser);
                }
            }

        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithExistingPrincipal3() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        assertFalse(uid.equals(p.getName()));

        User u = null;
        try {
        	// create a user with the given ID
            u = userMgr.createUser(uid, buildPassword(uid), p, null);
            save(superuser);

            // assert AuthorizableExistsException for principal that is already in use
            Group gr = null;
            try {
            	gr = userMgr.createGroup(getTestPrincipal().getName(), p, null);
            	fail("Principal " + p.getName() + " is already in use -> must throw AuthorizableExistsException.");
            } catch (AuthorizableExistsException aee) {
            	// expected this
            } finally {
                if (gr != null) {
                    gr.remove();
                    save(superuser);
                }
            }

        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithExistingUserID() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        User u = null;
        try {
        	// create a user with the given ID
            u = userMgr.createUser(uid, buildPassword(uid), p, null);
            save(superuser);
            
            // assert AuthorizableExistsException for id that is already in use
            Group gr = null;
            try {
            	gr = userMgr.createGroup(uid);
            	fail("ID " + uid + " is already in use -> must throw AuthorizableExistsException.");
            } catch (AuthorizableExistsException aee) {
            	// expected this
            } finally {
                if (gr != null) {
                    gr.remove();
                    save(superuser);
                }
            }
            
        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithExistingGroupID() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        Group g = null;
        try {
        	// create a user with the given ID
            g = userMgr.createGroup(uid);
            save(superuser);

            // assert AuthorizableExistsException for id that is already in use
            Group gr = null;
            try {
            	gr = userMgr.createGroup(uid);
            	fail("ID " + uid + " is already in use -> must throw AuthorizableExistsException.");
            } catch (AuthorizableExistsException aee) {
            	// expected this
            } finally {
                if (gr != null) {
                    gr.remove();
                    save(superuser);
                }
            }

        } finally {
            if (g != null) {
                g.remove();
                save(superuser);
            }
        }
    }

    public void testCreateGroupWithExistingGroupID2() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        String uid = getTestUserId(p);

        Group g = null;
        try {
        	// create a user with the given ID
            g = userMgr.createGroup(uid, p, null);
            save(superuser);

            // assert AuthorizableExistsException for id that is already in use
            Group gr = null;
            try {
            	gr = userMgr.createGroup(uid, getTestPrincipal(), null);
            	fail("ID " + uid + " is already in use -> must throw AuthorizableExistsException.");
            } catch (AuthorizableExistsException aee) {
            	// expected this
            } finally {
                if (gr != null) {
                    gr.remove();
                    save(superuser);
                }
            }

        } finally {
            if (g != null) {
                g.remove();
                save(superuser);
            }
        }
    }

    public void testFindAuthorizable() throws RepositoryException, NotExecutableException {
        Authorizable auth;
        Set<Principal> principals = getPrincipalSetFromSession(superuser);
        for (Principal p : principals) {
            auth = userMgr.getAuthorizable(p);

            if (auth != null) {
                if (!auth.isGroup() && auth.hasProperty(pPrincipalName)) {
                    String val = auth.getProperty(pPrincipalName)[0].getString();
                    Iterator<Authorizable> users = userMgr.findAuthorizables(pPrincipalName, val);

                    // the result must contain 1 authorizable
                    assertTrue(users.hasNext());
                    Authorizable first = users.next();
                    assertEquals(first.getID(), val);

                    // since id is unique -> there should be no more users in
                    // the iterator left
                    assertFalse(users.hasNext());
                }
            }
        }
    }

    public void testFindAuthorizableByAddedProperty() throws RepositoryException, NotExecutableException {
        Principal p = getTestPrincipal();
        Authorizable auth = null;

        try {
            auth= userMgr.createGroup(p);
            auth.setProperty("E-Mail", new Value[] { superuser.getValueFactory().createValue("anyVal")});
            save(superuser);

            boolean found = false;
            Iterator<Authorizable> result = userMgr.findAuthorizables("E-Mail", "anyVal");
            while (result.hasNext()) {
                Authorizable a = result.next();
                if (a.getID().equals(auth.getID())) {
                    found = true;
                }
            }

            assertTrue(found);
        } finally {
            // remove the create group again.
            if (auth != null) {
                auth.remove();
                save(superuser);
            }
        }
    }

    public void testFindAuthorizableByRelativePath() throws NotExecutableException, RepositoryException {
        Principal p = getTestPrincipal();
        Authorizable auth = null;

        try {
            auth= userMgr.createGroup(p);
            Value[] vs = new Value[] {
                    superuser.getValueFactory().createValue("v1"),
                    superuser.getValueFactory().createValue("v2")
            };

            String relPath = "relPath/" + propertyName1;
            String relPath2 = "another/" + propertyName1;
            String relPath3 = "relPath/relPath/" + propertyName1;
            auth.setProperty(relPath, vs);
            auth.setProperty(relPath2, vs);
            auth.setProperty(relPath3, superuser.getValueFactory().createValue("v3"));
            save(superuser);

            // relPath = "prop1", v = "v1" -> should find the target group
            Iterator<Authorizable> result = userMgr.findAuthorizables(propertyName1, "v1");
            assertTrue("expected result", result.hasNext());
            assertEquals(auth.getID(), result.next().getID());
            assertFalse("expected no more results", result.hasNext());

            // relPath = "prop1", v = "v1" -> should find the target group
            result = userMgr.findAuthorizables(propertyName1, "v3");
            assertTrue("expected result", result.hasNext());
            assertEquals(auth.getID(), result.next().getID());
            assertFalse("expected no more results", result.hasNext());

            // relPath = "relPath/prop1", v = "v1" -> should find the target group
            result = userMgr.findAuthorizables(relPath, "v1");
            assertTrue("expected result", result.hasNext());
            assertEquals(auth.getID(), result.next().getID());
            assertFalse("expected no more results", result.hasNext());

            // relPath : "./prop1", v = "v1" -> should not find the target group
            result = userMgr.findAuthorizables("./" + propertyName1, "v1");
            assertFalse("expected result", result.hasNext());
            
        } finally {
            // remove the create group again.
            if (auth != null) {
                auth.remove();
                save(superuser);
            }
        }
    }

    public void testFindUser() throws RepositoryException, NotExecutableException {
        User u = null;
        try {
            Principal p = getTestPrincipal();
            String uid = "UID" + p.getName();
            u = userMgr.createUser(uid, buildPassword(uid), p, null);
            save(superuser);

            boolean found = false;
            Iterator<Authorizable> it = userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_USER);
            while (it.hasNext() && !found) {
                User nu = (User) it.next();
                found = nu.getID().equals(uid);
            }
            assertTrue("Searching for 'null' must find the created user.", found);

            it = userMgr.findAuthorizables(pPrincipalName, p.getName(), UserManager.SEARCH_TYPE_USER);
            found = false;
            while (it.hasNext() && !found) {
                User nu = (User) it.next();
                found = nu.getPrincipal().getName().equals(p.getName());
            }
            assertTrue("Searching for principal-name must find the created user.", found);

            // but search groups should not find anything
            it = userMgr.findAuthorizables(pPrincipalName, p.getName(), UserManager.SEARCH_TYPE_GROUP);
            assertFalse(it.hasNext());

            it = userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_GROUP);
            while (it.hasNext()) {
                if (it.next().getPrincipal().getName().equals(p.getName())) {
                    fail("Searching for Groups should never find a user");
                }
            }
        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
        }
    }

    public void testFindGroup() throws RepositoryException, NotExecutableException {
        Group gr = null;
        try {
            Principal p = getTestPrincipal();
            gr = userMgr.createGroup(p);
            save(superuser);

            boolean found = false;
            Iterator<Authorizable> it = userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_GROUP);
            while (it.hasNext() && !found) {
                Group ng = (Group) it.next();
                found = ng.getPrincipal().getName().equals(p.getName());
            }
            assertTrue("Searching for \"\" must find the created group.", found);

            it = userMgr.findAuthorizables(pPrincipalName, p.getName(), UserManager.SEARCH_TYPE_GROUP);
            assertTrue(it.hasNext());
            Group ng = (Group) it.next();
            assertEquals("Searching for principal-name must find the created group.", p.getName(), ng.getPrincipal().getName());
            assertFalse("Only a single group must be found for a given principal name.", it.hasNext());

            // but search users should not find anything
            it = userMgr.findAuthorizables(pPrincipalName, p.getName(), UserManager.SEARCH_TYPE_USER);
            assertFalse(it.hasNext());

            it = userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_USER);
            while (it.hasNext()) {
                if (it.next().getPrincipal().getName().equals(p.getName())) {
                    fail("Searching for Users should never find a group");
                }
            }
        } finally {
            if (gr != null) {
                gr.remove();
                save(superuser);
            }
        }
    }

    public void testFindAllUsers() throws RepositoryException {
        Iterator<Authorizable> it = userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_USER);
        while (it.hasNext()) {
            assertFalse(it.next().isGroup());
        }
    }

    public void testFindAllGroups() throws RepositoryException {
        Iterator<Authorizable> it = userMgr.findAuthorizables(pPrincipalName, null, UserManager.SEARCH_TYPE_GROUP);
        while (it.hasNext()) {
            assertTrue(it.next().isGroup());
        }
    }

    public void testNewUserCanLogin() throws RepositoryException, NotExecutableException {
        String uid = getTestPrincipal().getName();
        String pw = buildPassword(uid);

        User u = null;
        Session s = null;
        try {
            u = userMgr.createUser(uid, pw);
            save(superuser);

            Credentials creds = new SimpleCredentials(uid, pw.toCharArray());
            s = superuser.getRepository().login(creds);
        } finally {
            if (u != null) {
                u.remove();
                save(superuser);
            }
            if (s != null) {
                s.logout();
            }
        }
    }

    public void testUnknownUserLogin() throws RepositoryException {
        String uid = getTestPrincipal().getName();
        assertNull(userMgr.getAuthorizable(uid));
        try {
            Session s = superuser.getRepository().login(new SimpleCredentials(uid, uid.toCharArray()));
            s.logout();

            fail("An unknown user should not be allowed to execute the login.");
        } catch (Exception e) {
            // ok.
        }
    }

    public void testCleanup() throws RepositoryException, NotExecutableException {
        Session s = getHelper().getSuperuserSession();
        try {
            UserManager umgr = getUserManager(s);
            s.logout();

            // after logging out the session, the user manager must have been
            // released as well and it's underlying session must not be available
            // any more -> accessing users must fail.
            try {
                umgr.getAuthorizable("any userid");
                fail("After having logged out the original session, the user manager must not be live any more.");
            } catch (RepositoryException e) {
                // success
            }
        } finally {
            if (s.isLive()) {
                s.logout();
            }
        }
    }

    public void testCleanupForAllWorkspaces() throws RepositoryException, NotExecutableException {
        String[] workspaceNames = superuser.getWorkspace().getAccessibleWorkspaceNames();

        for (String workspaceName1 : workspaceNames) {
            Session s = getHelper().getSuperuserSession(workspaceName1);
            try {
                UserManager umgr = getUserManager(s);
                s.logout();

                // after logging out the session, the user manager must have been
                // released as well and it's underlying session must not be available
                // any more -> accessing users must fail.
                try {
                    umgr.getAuthorizable("any userid");
                    fail("After having logged out the original session, the user manager must not be live any more.");
                } catch (RepositoryException e) {
                    // success
                }
            } finally {
                if (s.isLive()) {
                    s.logout();
                }
            }
        }
    }

    /**
     * Implementation specific test: user(/groups) cannot be nested.
     * @throws RepositoryException
     */
    public void testEnforceAuthorizableFolderHierarchy() throws RepositoryException {
        AuthorizableImpl authImpl = (AuthorizableImpl) userMgr.getAuthorizable(superuser.getUserID());
        Node userNode = authImpl.getNode();
        SessionImpl sImpl = (SessionImpl) userNode.getSession();

        Node folder = userNode.addNode("folder", sImpl.getJCRName(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
        String path = folder.getPath();
        try {
            // authNode - authFolder -> create User
            Authorizable a = null;
            try {
                Principal p = getTestPrincipal();
                a = userMgr.createUser(p.getName(), p.getName(), p, path);
                fail("Users may not be nested.");
            } catch (RepositoryException e) {
                // success
            } finally {
                if (a != null) {
                    a.remove();
                }
            }
        } finally {
            if (sImpl.nodeExists(path)) {
                folder.remove();
                sImpl.save();
            }
        }

        Node someContent = userNode.addNode("mystuff", "nt:unstructured");
        path = someContent.getPath();
        try {
            // authNode - anyNode -> create User
            Authorizable a = null;
            try {
                Principal p = getTestPrincipal();
                a = userMgr.createUser(p.getName(), p.getName(), p, someContent.getPath());
                fail("Users may not be nested.");
            } catch (RepositoryException e) {
                // success
            } finally {
                if (a != null) {
                    a.remove();
                    a = null;
                }
            }

            // authNode - anyNode - authFolder -> create User
            if (!sImpl.nodeExists(path)) {
                someContent = userNode.addNode("mystuff", "nt:unstructured");               
            }
            folder = someContent.addNode("folder", sImpl.getJCRName(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
            sImpl.save(); // this time save node structure
            try {
                Principal p = getTestPrincipal();
                a = userMgr.createUser(p.getName(), p.getName(), p, folder.getPath());
                fail("Users may not be nested.");
            } catch (RepositoryException e) {
                // success
            } finally {
                if (a != null) {
                    a.remove();
                }
            }
        } finally {
            if (sImpl.nodeExists(path)) {
                someContent.remove();
                sImpl.save();
            }
        }
    }

    public void testCreateWithRelativePath() throws Exception {
        Principal p = getTestPrincipal();
        String uid = p.getName();

        String usersPath = ((UserManagerImpl) userMgr).getUsersPath();

        List<String> invalid = new ArrayList<String>();
        invalid.add("../../path");
        invalid.add(usersPath + "/../test");

        for (String path : invalid) {
            try {
                User user = userMgr.createUser(uid, buildPassword(uid), p, path);
                save(superuser);

                fail("intermediate path may not point outside of the user tree.");
                user.remove();
                save(superuser);
                
            } catch (Exception e) {
                // success
                assertNull(userMgr.getAuthorizable(uid));
            }
        }
    }
}
