### Eclipse Workspace Patch 1.0 #P oak-core Index: src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java (revision 1504920) +++ src/main/java/org/apache/jackrabbit/oak/security/user/UserProvider.java (working copy) @@ -20,8 +20,12 @@ import java.text.ParseException; import java.util.Collections; import java.util.Iterator; +import java.util.Set; + import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.jcr.AccessDeniedException; import javax.jcr.RepositoryException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.query.Query; @@ -35,11 +39,16 @@ import org.apache.jackrabbit.oak.namepath.NamePathMapper; import org.apache.jackrabbit.oak.spi.query.PropertyValues; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.SecurityProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.AccessControlConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions; import org.apache.jackrabbit.oak.spi.security.user.AuthorizableNodeName; import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; import org.apache.jackrabbit.oak.spi.security.user.util.UserUtility; import org.apache.jackrabbit.oak.util.NodeUtil; +import org.apache.jackrabbit.oak.util.SecureNodeUtil; import org.apache.jackrabbit.util.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -166,10 +175,15 @@ private final String groupPath; private final String userPath; + + private final SecurityProvider securityProvider; + private PermissionProvider permissionProvider; - UserProvider(Root root, ConfigurationParameters config) { + UserProvider(Root root, ConfigurationParameters config, SecurityProvider securityProvider) { super(root, config); + this.securityProvider = securityProvider; + defaultDepth = config.getConfigValue(PARAM_DEFAULT_DEPTH, DEFAULT_DEPTH); groupPath = config.getConfigValue(PARAM_GROUP_PATH, DEFAULT_GROUP_PATH); userPath = config.getConfigValue(PARAM_USER_PATH, DEFAULT_USER_PATH); @@ -271,18 +285,22 @@ */ private NodeUtil createFolderNodes(String authorizableId, String nodeName, boolean isGroup, String intermediatePath) throws RepositoryException { - String authRoot = (isGroup) ? groupPath : userPath; - NodeUtil folder; - Tree authTree = root.getTree(authRoot); - if (!authTree.exists()) { - folder = new NodeUtil(root.getTree("/")); - for (String name : Text.explode(authRoot, '/', false)) { - folder = folder.getOrAddChild(name, NT_REP_AUTHORIZABLE_FOLDER); - } - } else { - folder = new NodeUtil(authTree); - } + + String authRoot = (isGroup) ? groupPath : userPath; + NodeUtil folder; + Tree authTree = root.getTree(authRoot); + if (!authTree.exists()) { + SecureNodeUtil secureFolder = new SecureNodeUtil(root,root.getTree("/")); + for (String name : Text.explode(authRoot, '/', false)) { + NodeUtil nodeUtil = secureFolder.getOrAddChild(getPermissionProvider(),name, NT_REP_AUTHORIZABLE_FOLDER); + secureFolder = new SecureNodeUtil(root, nodeUtil.getTree()); + } + folder = secureFolder; + } else { + folder = new NodeUtil(authTree); + } + // verification of hierarchy and node types is delegated to UserValidator upon commit String folderPath = getFolderPath(authorizableId, intermediatePath, authRoot); folder = folder.getOrAddTree(folderPath, NT_REP_AUTHORIZABLE_FOLDER); @@ -336,4 +354,20 @@ AuthorizableNodeName generator = config.getConfigValue(PARAM_AUTHORIZABLE_NODE_NAME, AuthorizableNodeName.DEFAULT); return generator.generateNodeName(authorizableId); } + + + @Nonnull + private Set getPrincipals() { + return root.getContentSession().getAuthInfo().getPrincipals(); + } + + @Nonnull + private PermissionProvider getPermissionProvider() { + if (permissionProvider == null) { + permissionProvider = securityProvider.getConfiguration(AccessControlConfiguration.class).getPermissionProvider(root, getPrincipals()); + } else { + permissionProvider.refresh(); + } + return permissionProvider; + } } \ No newline at end of file Index: src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java (revision 1504920) +++ src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java (working copy) @@ -81,7 +81,7 @@ UserConfiguration uc = securityProvider.getConfiguration(UserConfiguration.class); this.config = uc.getParameters(); - this.userProvider = new UserProvider(root, config); + this.userProvider = new UserProvider(root, config,securityProvider); this.membershipProvider = new MembershipProvider(root, config); this.authorizableActions = uc.getAuthorizableActionProvider().getAuthorizableActions(); } Index: src/main/java/org/apache/jackrabbit/oak/util/SecureNodeUtil.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/util/SecureNodeUtil.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/util/SecureNodeUtil.java (revision 0) @@ -0,0 +1,75 @@ +/* + * 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.util; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.jcr.AccessDeniedException; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.core.ImmutableRoot; +import org.apache.jackrabbit.oak.core.TreeTypeProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions; + +/** + * Utility class for accessing and writing typed content of a tree. + */ +public class SecureNodeUtil extends NodeUtil{ + + private Root root; + + public SecureNodeUtil(Root root, Tree tree) { + super(tree); + this.root = getImmutableRoot(root); + } + + @Nonnull + public NodeUtil getOrAddChild(PermissionProvider permissionProvider,String name, String primaryTypeName) throws AccessDeniedException { + NodeUtil child = getChild(permissionProvider,name); + return (child != null) ? child : addChild(permissionProvider,name, primaryTypeName); + } + + + @CheckForNull + public NodeUtil getChild(PermissionProvider permissionProvider,String name) throws AccessDeniedException { + checkPermissions(permissionProvider, root.getTree("/"+name), Permissions.READ_NODE); + return super.getChild(name); + } + + @Nonnull + public NodeUtil addChild(PermissionProvider permissionProvider,String name, String primaryNodeTypeName) throws AccessDeniedException { + checkPermissions(permissionProvider, root.getTree("/"+name), Permissions.ADD_NODE); + return super.addChild(name, primaryNodeTypeName); + } + + private void checkPermissions(PermissionProvider permissionProvider,@Nullable Tree tree, long permissions) throws AccessDeniedException { + boolean isGranted = permissionProvider.isGranted(tree, null, permissions); + if (!isGranted) { + throw new AccessDeniedException("Access denied."); + } + } + + private ImmutableRoot getImmutableRoot(Root root) { + if (root instanceof ImmutableRoot) { + return (ImmutableRoot) root; + } else { + return new ImmutableRoot(root, TreeTypeProvider.EMPTY); + } + } + } Index: src/test/java/org/apache/jackrabbit/oak/security/user/UserProviderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/security/user/UserProviderTest.java (revision 1493017) +++ src/test/java/org/apache/jackrabbit/oak/security/user/UserProviderTest.java (working copy) @@ -77,13 +77,13 @@ } private UserProvider createUserProvider() { - return new UserProvider(root, defaultConfig); + return new UserProvider(root, defaultConfig, new OpenSecurityProvider()); } private UserProvider createUserProvider(int defaultDepth) { Map options = new HashMap(customOptions); options.put(UserConstants.PARAM_DEFAULT_DEPTH, defaultDepth); - return new UserProvider(root, new ConfigurationParameters(options)); + return new UserProvider(root, new ConfigurationParameters(options), new OpenSecurityProvider()); } @Test Index: src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java (revision 1505660) +++ src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java (working copy) @@ -16,13 +16,20 @@ */ package org.apache.jackrabbit.oak.security.user; +import java.io.IOException; +import java.security.Principal; +import java.util.Set; + import javax.annotation.Nonnull; import javax.jcr.RepositoryException; +import javax.security.auth.Subject; import com.google.common.base.Strings; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.oak.api.AuthInfo; import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.core.RootImpl; import org.apache.jackrabbit.oak.namepath.NamePathMapper; @@ -41,12 +48,14 @@ import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch; import org.apache.jackrabbit.oak.util.NodeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; /** * Creates initial set of users to be present in a given workspace. This @@ -99,8 +108,9 @@ } catch (CommitFailedException e) { throw new RuntimeException(e); } - // TODO reconsider - Root root = new RootImpl(store, commitHook, PostCommitHook.EMPTY, workspaceName, SystemSubject.INSTANCE, new OpenSecurityProvider(), indexProvider); + + + Root root = new SystemRootImpl(store, commitHook, PostCommitHook.EMPTY, workspaceName, SystemSubject.INSTANCE, securityProvider, indexProvider); UserConfiguration userConfiguration = securityProvider.getConfiguration(UserConfiguration.class); UserManager userManager = userConfiguration.getUserManager(root, NamePathMapper.DEFAULT); @@ -144,4 +154,83 @@ } return store.getRoot(); } + + //------------------------- inner class ------------------- + + private class SystemRootImpl extends RootImpl { + + private final String workspaceName; + private final Subject subject; + + + + public SystemRootImpl(NodeStore store, CommitHook hook, + PostCommitHook postHook, String workspaceName, Subject subject, + SecurityProvider securityProvider, + QueryIndexProvider indexProvider) { + + super(store, hook, postHook, workspaceName, subject, + securityProvider, indexProvider); + + this.workspaceName = workspaceName; + this.subject = subject; + + } + + @Override + public ContentSession getContentSession() { + return new ContentSession() { + + private volatile boolean live = true; + + private void checkLive() { + checkState(live, "This session has been closed"); + } + + @Override + public void close() throws IOException { + live = false; + } + + @Override + public String getWorkspaceName() { + return workspaceName; + } + + @Override + public Root getLatestRoot() { + checkLive(); + return SystemRootImpl.this; + } + + @Override + public AuthInfo getAuthInfo() { + return new AuthInfo() { + + @Override + public String getUserID() { + return "systemUser"; + } + + @Override + public Set getPrincipals() { + return subject.getPrincipals(); + } + + @Override + public String[] getAttributeNames() { + return new String[] {}; + } + + @Override + public Object getAttribute(String attributeName) { + return null; + } + }; + } + }; + } + + } + }