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 2013/10/25 21:21:00 UTC

svn commit: r1535831 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/security/user/autosave/ test/java/org/apache/jackrabbit/oak/security/user/autosave/

Author: angela
Date: Fri Oct 25 19:21:00 2013
New Revision: 1535831

URL: http://svn.apache.org/r1535831
Log:
OAK-50 : Implement User Management

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableWrapper.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/GroupImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/UserImpl.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableImpl.java?rev=1535831&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableImpl.java Fri Oct 25 19:21:00 2013
@@ -0,0 +1,131 @@
+/*
+ * 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.autosave;
+
+import java.security.Principal;
+import java.util.Iterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class AuthorizableImpl implements Authorizable {
+
+    /**
+     * logger instance
+     */
+    private static final Logger log = LoggerFactory.getLogger(AuthorizableImpl.class);
+
+    final Authorizable dlg;
+    final AutoSaveEnabledManager mgr;
+
+    AuthorizableImpl(Authorizable dlg, AutoSaveEnabledManager mgr) {
+        this.dlg = dlg;
+        this.mgr = mgr;
+    }
+
+    @Override
+    public String getID() throws RepositoryException {
+        return dlg.getID();
+    }
+
+    @Override
+    public boolean isGroup() {
+        return dlg.isGroup();
+    }
+
+    @Override
+    public Principal getPrincipal() throws RepositoryException {
+        return dlg.getPrincipal();
+    }
+
+    @Override
+    public Iterator<Group> declaredMemberOf() throws RepositoryException {
+        return AuthorizableWrapper.createGroupIterator(dlg.declaredMemberOf(), mgr);
+    }
+
+    @Override
+    public Iterator<Group> memberOf() throws RepositoryException {
+        return AuthorizableWrapper.createGroupIterator(dlg.memberOf(), mgr);
+    }
+
+    @Override
+    public void remove() throws RepositoryException {
+        try {
+            dlg.remove();
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public Iterator<String> getPropertyNames() throws RepositoryException {
+        return dlg.getPropertyNames();
+    }
+
+    @Override
+    public Iterator<String> getPropertyNames(String s) throws RepositoryException {
+        return dlg.getPropertyNames(s);
+
+    }
+
+    @Override
+    public boolean hasProperty(String s) throws RepositoryException {
+        return dlg.hasProperty(s);
+    }
+
+    @Override
+    public void setProperty(String s, Value value) throws RepositoryException {
+        try {
+            dlg.setProperty(s, value);
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public void setProperty(String s, Value[] values) throws RepositoryException {
+        try {
+            dlg.setProperty(s, values);
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public Value[] getProperty(String s) throws RepositoryException {
+        return dlg.getProperty(s);
+    }
+
+    @Override
+    public boolean removeProperty(String s) throws RepositoryException {
+        try {
+            return dlg.removeProperty(s);
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public String getPath() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return dlg.getPath();
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableWrapper.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableWrapper.java?rev=1535831&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableWrapper.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AuthorizableWrapper.java Fri Oct 25 19:21:00 2013
@@ -0,0 +1,53 @@
+/*
+ * 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.autosave;
+
+import java.util.Iterator;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterators;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+
+class AuthorizableWrapper<T extends Authorizable> implements Function<T, T> {
+
+    private final AutoSaveEnabledManager mgr;
+
+    private AuthorizableWrapper(AutoSaveEnabledManager mgr) {
+        this.mgr = mgr;
+    }
+
+    @Override
+    public T apply(T authorizable) {
+        if (authorizable == null) {
+            return null;
+        } else if (authorizable.isGroup()) {
+            return (T) new GroupImpl((Group) authorizable, mgr);
+        } else {
+            return (T) new UserImpl((User) authorizable, mgr);
+        }
+    }
+
+    static Iterator<Authorizable> createIterator(Iterator<Authorizable> dlgs, AutoSaveEnabledManager mgr) {
+        return Iterators.transform(dlgs, new AuthorizableWrapper(mgr));
+    }
+
+    static Iterator<Group> createGroupIterator(Iterator<Group> dlgs, AutoSaveEnabledManager mgr) {
+        return Iterators.transform(dlgs, new AuthorizableWrapper(mgr));
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManager.java?rev=1535831&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManager.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManager.java Fri Oct 25 19:21:00 2013
@@ -0,0 +1,102 @@
+/*
+ * 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.autosave;
+
+import java.security.Principal;
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.security.user.UserManagerImpl;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+
+/**
+ * Implementation of the user management that allows to set the autosave flag.
+ * Since OAK does no longer support the auto-save flag out of the box and this
+ * part of the user management is targeted for deprecation, this {@code UserManager}
+ * implementation should only be used for those cases where strict backwards
+ * compatibility is really required.
+ *
+ * <p>In general any consumer of the Jackrabbit user management API should stick
+ * to the API contract and verify that the autosave flag is enabled before
+ * relying on the implementation to have it turned on:</p>
+ *
+ * <pre>
+ *     JackrabbitSession session = ...;
+ *     UserManager userManager = session.getUserManager();
+ *
+ *     // modify some user related content
+ *
+ *     if (!userManager#isAutosave()) {
+ *         session.save();
+ *     }
+ * </pre>
+ */
+public class AutoSaveEnabledManager extends UserManagerImpl {
+
+    private final Root root;
+    private boolean autosave = true;
+
+    public AutoSaveEnabledManager(Root root, NamePathMapper namePathMapper, SecurityProvider securityProvider) {
+        super(root, namePathMapper, securityProvider);
+        this.root = root;
+    }
+
+    @Override
+    public User createUser(String userID, String password, Principal principal, @Nullable String intermediatePath) throws RepositoryException {
+        try {
+            return new UserImpl(super.createUser(userID, password, principal, intermediatePath), this);
+        } finally {
+            autosave();
+        }
+    }
+
+    @Override
+    public Group createGroup(String groupID, Principal principal, @Nullable String intermediatePath) throws RepositoryException {
+        try {
+            return new GroupImpl(super.createGroup(groupID, principal, intermediatePath), this);
+        } finally {
+            autosave();
+        }
+    }
+
+    @Override
+    public boolean isAutoSave() {
+        return autosave;
+    }
+
+    @Override
+    public void autoSave(boolean enable) throws RepositoryException {
+        autosave = enable;
+    }
+
+    void autosave() throws RepositoryException {
+        if (autosave) {
+            try {
+                root.commit();
+            } catch (CommitFailedException e) {
+                throw e.asRepositoryException();
+            } finally {
+                root.refresh();
+            }
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/GroupImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/GroupImpl.java?rev=1535831&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/GroupImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/GroupImpl.java Fri Oct 25 19:21:00 2013
@@ -0,0 +1,92 @@
+/*
+ * 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.autosave;
+
+import java.util.Iterator;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+
+class GroupImpl extends AuthorizableImpl implements Group {
+
+    GroupImpl(Group dlg, AutoSaveEnabledManager mgr) {
+        super(dlg, mgr);
+    }
+
+    private Group getDelegate() {
+        return (Group) dlg;
+    }
+
+    @Override
+    public Iterator<Authorizable> getDeclaredMembers() throws RepositoryException {
+        return AuthorizableWrapper.createIterator(getDelegate().getDeclaredMembers(), mgr);
+    }
+
+    @Override
+    public Iterator<Authorizable> getMembers() throws RepositoryException {
+        return AuthorizableWrapper.createIterator(getDelegate().getMembers(), mgr);
+    }
+
+    @Override
+    public boolean isDeclaredMember(Authorizable authorizable) throws RepositoryException {
+        if (isValid(authorizable)) {
+            return getDelegate().isDeclaredMember(((AuthorizableImpl) authorizable).dlg);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isMember(Authorizable authorizable) throws RepositoryException {
+        if (isValid(authorizable)) {
+            return getDelegate().isMember(((AuthorizableImpl) authorizable).dlg);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean addMember(Authorizable authorizable) throws RepositoryException {
+        try {
+            if (isValid(authorizable)) {
+                return getDelegate().addMember(((AuthorizableImpl) authorizable).dlg);
+            } else {
+                return false;
+            }
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public boolean removeMember(Authorizable authorizable) throws RepositoryException {
+        try {
+            if (isValid(authorizable)) {
+                return getDelegate().removeMember(((AuthorizableImpl) authorizable).dlg);
+            } else {
+                return false;
+            }
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    private boolean isValid(Authorizable a) {
+        return a instanceof AuthorizableImpl;
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/UserImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/UserImpl.java?rev=1535831&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/UserImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/autosave/UserImpl.java Fri Oct 25 19:21:00 2013
@@ -0,0 +1,126 @@
+/*
+ * 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.autosave;
+
+import java.security.Principal;
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.security.auth.Subject;
+
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
+import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.api.security.user.User;
+
+class UserImpl extends AuthorizableImpl implements User {
+
+    UserImpl(User dlg, AutoSaveEnabledManager mgr) {
+        super(dlg, mgr);
+    }
+
+    private User getDelegate() {
+        return (User) dlg;
+    }
+
+    @Override
+    public boolean isAdmin() {
+        return getDelegate().isAdmin();
+    }
+
+    @Override
+    public Credentials getCredentials() throws RepositoryException {
+        return getDelegate().getCredentials();
+    }
+
+    @Override
+    public Impersonation getImpersonation() throws RepositoryException {
+        return new ImpersonationImpl(getDelegate().getImpersonation());
+    }
+
+    @Override
+    public void changePassword(String pw) throws RepositoryException {
+        try {
+            getDelegate().changePassword(pw);
+        } finally {
+            mgr.autosave();
+        }
+
+    }
+
+    @Override
+    public void changePassword(String pw, String oldPw) throws RepositoryException {
+        try {
+            getDelegate().changePassword(pw, oldPw);
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public void disable(String msg) throws RepositoryException {
+        try {
+            getDelegate().disable(msg);
+        } finally {
+            mgr.autosave();
+        }
+    }
+
+    @Override
+    public boolean isDisabled() throws RepositoryException {
+        return getDelegate().isDisabled();
+    }
+
+    @Override
+    public String getDisabledReason() throws RepositoryException {
+        return getDelegate().getDisabledReason();
+    }
+
+    private class ImpersonationImpl implements Impersonation {
+
+        private final Impersonation dlg;
+
+        private ImpersonationImpl(Impersonation dlg) {
+            this.dlg = dlg;
+        }
+        @Override
+        public PrincipalIterator getImpersonators() throws RepositoryException {
+            return dlg.getImpersonators();
+        }
+
+        @Override
+        public boolean grantImpersonation(Principal principal) throws RepositoryException {
+            try {
+                return dlg.grantImpersonation(principal);
+            } finally {
+                mgr.autosave();
+            }
+        }
+
+        @Override
+        public boolean revokeImpersonation(Principal principal) throws RepositoryException {
+            try {
+                return dlg.revokeImpersonation(principal);
+            } finally {
+                mgr.autosave();
+            }
+        }
+
+        @Override
+        public boolean allows(Subject subject) throws RepositoryException {
+            return dlg.allows(subject);
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java?rev=1535831&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/autosave/AutoSaveEnabledManagerTest.java Fri Oct 25 19:21:00 2013
@@ -0,0 +1,208 @@
+/*
+ * 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.autosave;
+
+import java.security.Principal;
+import java.util.Iterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AutoSaveEnabledManagerTest extends AbstractSecurityTest {
+
+    private AutoSaveEnabledManager mgr;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+        mgr = new AutoSaveEnabledManager(root, getNamePathMapper(), getSecurityProvider());
+    }
+
+    @Override
+    public void after() throws Exception {
+        Authorizable a = mgr.getAuthorizable("u");
+        if (a != null) {
+            a.remove();
+        }
+        a = mgr.getAuthorizable("g");
+        if (a != null) {
+            a.remove();
+        }
+        if (root.hasPendingChanges()) {
+            root.commit();
+        }
+        super.after();
+    }
+
+    @Test
+    public void testIsAutoSave() {
+        assertTrue(mgr.isAutoSave());
+    }
+
+    @Test
+    public void testAutoSave() throws RepositoryException {
+        mgr.autoSave(false);
+        mgr.autoSave(true);
+    }
+
+    @Test
+    public void testCreateRemoveUser() throws RepositoryException {
+        User u = mgr.createUser("u", "u");
+        assertFalse(root.hasPendingChanges());
+        u.remove();
+        assertFalse(root.hasPendingChanges());
+    }
+
+    @Test
+    public void testCreateRemoveGroup() throws RepositoryException {
+        Group g = mgr.createGroup("g");
+        assertFalse(root.hasPendingChanges());
+        g.remove();
+        assertFalse(root.hasPendingChanges());
+    }
+
+    @Test
+    public void testCommitFailedRevertChanges() throws RepositoryException {
+        User u = mgr.createUser("u", "u");
+        try {
+            User u2 = mgr.createUser("u", "u");
+            fail();
+        } catch (RepositoryException e) {
+            // success
+            assertFalse(root.hasPendingChanges());
+        }
+    }
+
+    @Test
+    public void testAuthorizable() throws Exception {
+        User u = mgr.createUser("u", "u");
+        u.setProperty("prop", getValueFactory().createValue("value"));
+        assertFalse(root.hasPendingChanges());
+
+        u.setProperty("prop", new Value[] {getValueFactory().createValue(true)});
+        assertFalse(root.hasPendingChanges());
+
+        u.removeProperty("prop");
+        assertFalse(root.hasPendingChanges());
+    }
+
+    @Test
+    public void testUser() throws Exception {
+        User u = mgr.createUser("u", "u");
+
+        u.disable("disabled");
+        assertTrue(u.isDisabled());
+        assertFalse(root.hasPendingChanges());
+
+        u.disable(null);
+        assertFalse(u.isDisabled());
+        assertFalse(root.hasPendingChanges());
+
+        u.changePassword("t");
+        assertFalse(root.hasPendingChanges());
+
+        u.changePassword("tt", "t");
+        assertFalse(root.hasPendingChanges());
+    }
+
+    @Test
+    public void testImpersonation() throws Exception {
+        User u = mgr.createUser("u", "u");
+
+        Impersonation imp = u.getImpersonation();
+        Principal p = mgr.getAuthorizable("anonymous").getPrincipal();
+        assertTrue(imp.grantImpersonation(p));
+        assertFalse(root.hasPendingChanges());
+
+        assertTrue(imp.revokeImpersonation(p));
+        assertFalse(root.hasPendingChanges());
+    }
+
+    @Test
+    public void testGroup() throws Exception {
+        User u = mgr.createUser("u", "u");
+        Group g = mgr.createGroup("g");
+
+        assertTrue(g.addMember(u));
+        assertFalse(root.hasPendingChanges());
+        assertTrue(g.isDeclaredMember(u));
+
+        Iterator<Authorizable> it = g.getDeclaredMembers();
+        if (it.hasNext()) {
+            Authorizable a = it.next();
+            assertTrue(a instanceof AuthorizableImpl);
+            a.setProperty("prop", getValueFactory().createValue("blub"));
+            assertFalse(root.hasPendingChanges());
+        }
+
+        it = g.getMembers();
+        if (it.hasNext()) {
+            Authorizable a = it.next();
+            assertTrue(a instanceof AuthorizableImpl);
+            a.setProperty("prop", getValueFactory().createValue("blub"));
+            assertFalse(root.hasPendingChanges());
+        }
+
+        assertTrue(g.removeMember(u));
+        assertFalse(root.hasPendingChanges());
+        assertFalse(g.isDeclaredMember(u));
+    }
+
+    @Test
+    public void testDeclaredMemberOf() throws Exception {
+        User u = mgr.createUser("u", "u");
+        Group g = mgr.createGroup("g");
+
+        assertTrue(g.addMember(u));
+
+        Iterator<Group> groups = u.declaredMemberOf();
+        assertTrue(groups.hasNext());
+
+        Group gAgain = groups.next();
+        assertTrue(gAgain instanceof GroupImpl);
+        assertTrue(gAgain.removeMember(u));
+        assertFalse(root.hasPendingChanges());
+        assertFalse(u.declaredMemberOf().hasNext());
+    }
+
+    @Test
+    public void testMemberOf() throws Exception {
+        User u = mgr.createUser("u", "u");
+        Group g = mgr.createGroup("g");
+
+        assertTrue(g.addMember(u));
+
+        Iterator<Group> groups = u.memberOf();
+        assertTrue(groups.hasNext());
+
+        Group gAgain = groups.next();
+        assertTrue(gAgain instanceof GroupImpl);
+        assertTrue(gAgain.removeMember(u));
+        assertFalse(root.hasPendingChanges());
+        assertFalse(u.declaredMemberOf().hasNext());
+    }
+}
\ No newline at end of file