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 2012/10/31 14:40:15 UTC
svn commit: r1404136 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/security/user/
main/java/org/apache/jackrabbit/oak/spi/security/user/util/
test/java/org/apache/jackrabbit/oak/security/user/
Author: angela
Date: Wed Oct 31 13:40:15 2012
New Revision: 1404136
URL: http://svn.apache.org/viewvc?rev=1404136&view=rev
Log:
OAK-50 : Implement User Management (WIP)
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AuthorizableBaseProvider.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/UserUtility.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AuthorizableBaseProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AuthorizableBaseProvider.java?rev=1404136&r1=1404135&r2=1404136&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AuthorizableBaseProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/AuthorizableBaseProvider.java Wed Oct 31 13:40:15 2012
@@ -57,11 +57,11 @@ abstract class AuthorizableBaseProvider
}
}
- String getContentID(String authorizableId) {
- return IdentifierManager.generateUUID(authorizableId.toLowerCase());
- }
-
String getContentID(Tree authorizableTree) {
return identifierManager.getIdentifier(authorizableTree);
}
+
+ static String getContentID(String authorizableId) {
+ return IdentifierManager.generateUUID(authorizableId.toLowerCase());
+ }
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java?rev=1404136&r1=1404135&r2=1404136&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java Wed Oct 31 13:40:15 2012
@@ -160,29 +160,28 @@ class UserProvider extends AuthorizableB
userPath = config.getConfigValue(PARAM_USER_PATH, DEFAULT_USER_PATH);
}
- //-------------------------------------------------------< UserProvider >---
@Nonnull
- public Tree createUser(String userID, String intermediateJcrPath) throws RepositoryException {
+ Tree createUser(String userID, String intermediateJcrPath) throws RepositoryException {
return createAuthorizableNode(userID, false, intermediateJcrPath);
}
@Nonnull
- public Tree createGroup(String groupID, String intermediateJcrPath) throws RepositoryException {
+ Tree createGroup(String groupID, String intermediateJcrPath) throws RepositoryException {
return createAuthorizableNode(groupID, true, intermediateJcrPath);
}
@CheckForNull
- public Tree getAuthorizable(String authorizableId) {
+ Tree getAuthorizable(String authorizableId) {
return getByID(authorizableId, AuthorizableType.AUTHORIZABLE);
}
@CheckForNull
- public Tree getAuthorizableByPath(String authorizableOakPath) {
+ Tree getAuthorizableByPath(String authorizableOakPath) {
return getByPath(authorizableOakPath);
}
@CheckForNull
- public Tree getAuthorizableByPrincipal(Principal principal) {
+ Tree getAuthorizableByPrincipal(Principal principal) {
if (principal instanceof TreeBasedPrincipal) {
return root.getTree(((TreeBasedPrincipal) principal).getOakPath());
}
@@ -213,7 +212,7 @@ class UserProvider extends AuthorizableB
}
@CheckForNull
- public String getAuthorizableId(Tree authorizableTree) {
+ static String getAuthorizableId(Tree authorizableTree) {
checkNotNull(authorizableTree);
if (UserUtility.isType(authorizableTree, AuthorizableType.AUTHORIZABLE)) {
PropertyState idProp = authorizableTree.getProperty(UserConstants.REP_AUTHORIZABLE_ID);
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java?rev=1404136&r1=1404135&r2=1404136&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserValidator.java Wed Oct 31 13:40:15 2012
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.securi
import javax.jcr.nodetype.ConstraintViolationException;
+import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
@@ -32,7 +33,10 @@ import org.apache.jackrabbit.oak.util.No
import org.apache.jackrabbit.util.Text;
/**
- * UserValidator... TODO
+ * Validator that enforces user management specific constraints. Please note that
+ * is this validator is making implementation specific assumptions; if the
+ * user management implementation is replace it is most probably necessary to
+ * provide a custom validator as well.
*/
class UserValidator extends DefaultValidator implements UserConstants {
@@ -52,24 +56,38 @@ class UserValidator extends DefaultValid
@Override
public void propertyAdded(PropertyState after) throws CommitFailedException {
+ if (!isAuthorizable(parentAfter)) {
+ return;
+ }
+
String name = after.getName();
if (REP_DISABLED.equals(name) && isAdminUser(parentAfter)) {
String msg = "Admin user cannot be disabled.";
fail(msg);
}
+
+ if (JcrConstants.JCR_UUID.equals(name) && !isValidUUID(after.getValue(Type.STRING))) {
+ String msg = "Invalid jcr:uuid for authorizable " + parentAfter.getName();
+ fail(msg);
+ }
}
@Override
public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException {
+ if (!isAuthorizable(parentAfter)) {
+ return;
+ }
+
String name = before.getName();
- if (UserUtility.isAuthorizableTree(parentBefore.getTree()) && (REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name))) {
+ if (REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name)) {
String msg = "Authorizable property " + name + " may not be altered after user/group creation.";
fail(msg);
+ } else if (JcrConstants.JCR_UUID.equals(name) && !isValidUUID(after.getValue(Type.STRING))) {
+ String msg = "Invalid jcr:uuid for authorizable " + parentAfter.getName();
+ fail(msg);
}
- if (UserUtility.isType(parentBefore.getTree(), AuthorizableType.USER)
- && REP_PASSWORD.equals(name)
- && PasswordUtility.isPlainTextPassword(after.getValue(Type.STRING))) {
+ if (isUser(parentBefore) && REP_PASSWORD.equals(name) && PasswordUtility.isPlainTextPassword(after.getValue(Type.STRING))) {
String msg = "Password may not be plain text.";
fail(msg);
}
@@ -78,9 +96,12 @@ class UserValidator extends DefaultValid
@Override
public void propertyDeleted(PropertyState before) throws CommitFailedException {
+ if (!isAuthorizable(parentAfter)) {
+ return;
+ }
+
String name = before.getName();
- if (UserUtility.isAuthorizableTree(parentBefore.getTree())
- && (REP_PASSWORD.equals(name) || REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name))) {
+ if (REP_PASSWORD.equals(name) || REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name)) {
String msg = "Authorizable property " + name + " may not be removed.";
fail(msg);
}
@@ -148,13 +169,28 @@ class UserValidator extends DefaultValid
}
}
+
// FIXME: copied from UserProvider#isAdminUser
private boolean isAdminUser(NodeUtil userNode) {
String id = (userNode.getString(REP_AUTHORIZABLE_ID, Text.unescapeIllegalJcrChars(userNode.getName())));
- return UserUtility.isType(userNode.getTree(), AuthorizableType.USER) &&
- UserUtility.getAdminId(provider.getConfig()).equals(id);
+ return isUser(userNode) && UserUtility.getAdminId(provider.getConfig()).equals(id);
+ }
+
+ private boolean isValidUUID(String uuid) {
+ String id = UserProvider.getAuthorizableId(parentAfter.getTree());
+ return uuid.equals(UserProvider.getContentID(id));
+ }
+
+ private static boolean isAuthorizable(NodeUtil node) {
+ return UserUtility.isType(node.getTree(), AuthorizableType.AUTHORIZABLE);
}
+ private static boolean isUser(NodeUtil node) {
+ return UserUtility.isType(node.getTree(), AuthorizableType.USER);
+ }
+
+
+
private static void fail(String msg) throws CommitFailedException {
Exception e = new ConstraintViolationException(msg);
throw new CommitFailedException(e);
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/UserUtility.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/UserUtility.java?rev=1404136&r1=1404135&r2=1404136&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/UserUtility.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/UserUtility.java Wed Oct 31 13:40:15 2012
@@ -41,10 +41,6 @@ public final class UserUtility implement
return parameters.getConfigValue(PARAM_ANONYMOUS_ID, DEFAULT_ANONYMOUS_ID);
}
- public static boolean isAuthorizableTree(Tree authorizableTree) {
- return isType(authorizableTree, AuthorizableType.AUTHORIZABLE);
- }
-
public static boolean isType(Tree authorizableTree, AuthorizableType type) {
// FIXME: check for node type according to the specified type constraint
if (authorizableTree != null && authorizableTree.hasProperty(JcrConstants.JCR_PRIMARYTYPE)) {
Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java?rev=1404136&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserValidatorTest.java Wed Oct 31 13:40:15 2012
@@ -0,0 +1,286 @@
+/*
+ * 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.List;
+import java.util.UUID;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+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.api.Tree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.security.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.util.Text;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+
+/**
+ * UserValidatorTest
+ */
+public class UserValidatorTest extends AbstractSecurityTest {
+
+ private Root root;
+ private UserManagerImpl userMgr;
+ private User user;
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ root = admin.getLatestRoot();
+ userMgr = new UserManagerImpl(null, root, NamePathMapper.DEFAULT, getSecurityProvider());
+ user = userMgr.createUser("test", "pw");
+ root.commit();
+ }
+
+ @After
+ public void after() throws Exception {
+ try {
+ Authorizable a = userMgr.getAuthorizable("test");
+ if (a != null) {
+ a.remove();
+ root.commit();
+ }
+ } finally {
+ super.after();
+ }
+ }
+
+ @Test
+ public void removePassword() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.removeProperty(UserConstants.REP_PASSWORD);
+ root.commit();
+ fail("removing password should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void removePrincipalName() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.removeProperty(UserConstants.REP_PRINCIPAL_NAME);
+ root.commit();
+ fail("removing principal name should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void removeAuthorizableId() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.removeProperty(UserConstants.REP_AUTHORIZABLE_ID);
+ root.commit();
+ fail("removing authorizable id should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void createWithoutPrincipalName() throws Exception {
+ try {
+ User user = userMgr.createUser("withoutPrincipalName", "pw");
+ // TODO: use user.getPath instead (blocked by OAK-343)
+ Tree tree = root.getTree("/rep:security/rep:authorizables/rep:users/t/te/test");
+ tree.removeProperty(UserConstants.REP_PRINCIPAL_NAME);
+ root.commit();
+
+ fail("creating user with invalid jcr:uuid should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void createWithInvalidUUID() throws Exception {
+ try {
+ User user = userMgr.createUser("withInvalidUUID", "pw");
+ // TODO: use user.getPath instead (blocked by OAK-343)
+ Tree tree = root.getTree("/rep:security/rep:authorizables/rep:users/t/te/test");
+ tree.setProperty(JcrConstants.JCR_UUID, UUID.randomUUID().toString());
+ root.commit();
+
+ fail("creating user with invalid jcr:uuid should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void changeUUID() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.setProperty(JcrConstants.JCR_UUID, UUID.randomUUID().toString());
+ root.commit();
+ fail("changing jcr:uuid should fail if it the uuid valid is invalid");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void changePrincipalName() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.setProperty(UserConstants.REP_PRINCIPAL_NAME, "another");
+ root.commit();
+ fail("changing the principal name should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void changeAuthorizableId() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.setProperty(UserConstants.REP_AUTHORIZABLE_ID, "modified");
+ root.commit();
+ fail("changing the authorizable id should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void changePasswordToPlainText() throws Exception {
+ try {
+ Tree userTree = root.getTree(user.getPath());
+ userTree.setProperty(UserConstants.REP_PASSWORD, "plaintext");
+ root.commit();
+ fail("storing a plaintext password should fail");
+ } catch (CommitFailedException e) {
+ // expected
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void testRemoveAdminUser() throws Exception {
+ try {
+ String adminId = userMgr.getConfig().getConfigValue(UserConstants.PARAM_ADMIN_ID, UserConstants.DEFAULT_ADMIN_ID);
+ Authorizable admin = userMgr.getAuthorizable(adminId);
+ if (admin == null) {
+ admin = userMgr.createUser(adminId, adminId);
+ root.commit();
+ }
+
+ root.getTree(admin.getPath()).remove();
+ root.commit();
+ fail("Admin user cannot be removed");
+ } catch (CommitFailedException e) {
+ // success
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void testDisableAdminUser() throws Exception {
+ try {
+ String adminId = userMgr.getConfig().getConfigValue(UserConstants.PARAM_ADMIN_ID, UserConstants.DEFAULT_ADMIN_ID);
+ Authorizable admin = userMgr.getAuthorizable(adminId);
+ if (admin == null) {
+ admin = userMgr.createUser(adminId, adminId);
+ root.commit();
+ }
+
+ root.getTree(admin.getPath()).setProperty(UserConstants.REP_DISABLED, "disabled");
+ root.commit();
+ fail("Admin user cannot be disabled");
+ } catch (CommitFailedException e) {
+ // success
+ } finally {
+ root.refresh();
+ }
+ }
+
+ @Test
+ public void testEnforceHierarchy() throws RepositoryException, CommitFailedException {
+ List<String> invalid = new ArrayList<String>();
+ invalid.add("/");
+ invalid.add("/jcr:system");
+ String groupPath = userMgr.getConfig().getConfigValue(UserConstants.PARAM_GROUP_PATH, UserConstants.DEFAULT_GROUP_PATH);
+ invalid.add(groupPath);
+ String userPath = userMgr.getConfig().getConfigValue(UserConstants.PARAM_USER_PATH, UserConstants.DEFAULT_USER_PATH);
+ invalid.add(Text.getRelativeParent(userPath, 1));
+ invalid.add(user.getPath());
+ invalid.add(user.getPath() + "/folder");
+
+ for (String path : invalid) {
+ try {
+ Tree parent = root.getTree(path);
+ if (parent == null) {
+ String[] segments = Text.explode(path, '/', false);
+ parent = root.getTree("/");
+ for (String segment : segments) {
+ Tree next = parent.getChild(segment);
+ if (next == null) {
+ next = parent.addChild(segment);
+ next.setProperty(JcrConstants.JCR_PRIMARYTYPE, UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
+ parent = next;
+ }
+ }
+ }
+ Tree userTree = parent.addChild("testUser");
+ userTree.setProperty(JcrConstants.JCR_PRIMARYTYPE, UserConstants.NT_REP_USER);
+ userTree.setProperty(JcrConstants.JCR_UUID, UserProvider.getContentID("testUser"));
+ userTree.setProperty(UserConstants.REP_PRINCIPAL_NAME, "testUser");
+ root.commit();
+ fail("Invalid hierarchy should be detected");
+
+ } catch (CommitFailedException e) {
+ // success
+ } finally {
+ root.refresh();
+ }
+ }
+ }
+
+}
\ No newline at end of file