Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2EA88106DF for ; Fri, 1 Nov 2013 00:59:33 +0000 (UTC) Received: (qmail 74606 invoked by uid 500); 1 Nov 2013 00:59:33 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 74577 invoked by uid 500); 1 Nov 2013 00:59:33 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-dev@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 74569 invoked by uid 99); 1 Nov 2013 00:59:33 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 01 Nov 2013 00:59:33 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 01 Nov 2013 00:59:29 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id D7E292388994; Fri, 1 Nov 2013 00:59:07 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1537792 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization... Date: Fri, 01 Nov 2013 00:59:07 -0000 To: oak-commits@jackrabbit.apache.org From: tripod@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20131101005907.D7E292388994@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tripod Date: Fri Nov 1 00:59:06 2013 New Revision: 1537792 URL: http://svn.apache.org/r1537792 Log: OAK-1138 Implement global per principal permission entry cache (wip) Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryCache.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProvider.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntries.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/MultipleSessionsACLStabilityTest.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHook.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStore.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionUtil.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImplTest.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreTest.java jackrabbit/oak/trunk/oak-run/run_concurrent.sh Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java Fri Nov 1 00:59:06 2013 @@ -17,11 +17,13 @@ package org.apache.jackrabbit.oak.security; import java.util.HashSet; +import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl; import org.apache.jackrabbit.oak.security.authorization.AuthorizationConfigurationImpl; +import org.apache.jackrabbit.oak.security.authorization.permission.PermissionEntryCache; import org.apache.jackrabbit.oak.security.principal.PrincipalConfigurationImpl; import org.apache.jackrabbit.oak.security.privilege.PrivilegeConfigurationImpl; import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl; @@ -38,6 +40,10 @@ public class SecurityProviderImpl implem private final ConfigurationParameters configuration; + // we only need 1 instance of authorization config. + // todo: maybe provide general mechanism to singletons of configs + private AuthorizationConfiguration authorizationConfiguration; + public SecurityProviderImpl() { this(ConfigurationParameters.EMPTY); } @@ -89,7 +95,10 @@ public class SecurityProviderImpl implem @Nonnull private AuthorizationConfiguration getAuthorizationConfiguration() { - return new AuthorizationConfigurationImpl(this); + if (authorizationConfiguration == null) { + authorizationConfiguration = new AuthorizationConfigurationImpl(this); + } + return authorizationConfiguration; } @Nonnull Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationConfigurationImpl.java Fri Nov 1 00:59:06 2013 @@ -25,8 +25,6 @@ import javax.annotation.Nonnull; import javax.jcr.security.AccessControlManager; import javax.security.auth.Subject; -import com.google.common.collect.ImmutableList; - import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.oak.api.Root; @@ -35,6 +33,7 @@ import org.apache.jackrabbit.oak.plugins import org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlImporter; import org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlManagerImpl; import org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlValidatorProvider; +import org.apache.jackrabbit.oak.security.authorization.permission.PermissionEntryCache; import org.apache.jackrabbit.oak.security.authorization.permission.PermissionHook; import org.apache.jackrabbit.oak.security.authorization.permission.PermissionProviderImpl; import org.apache.jackrabbit.oak.security.authorization.permission.PermissionStoreValidatorProvider; @@ -53,12 +52,16 @@ import org.apache.jackrabbit.oak.spi.sec import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter; +import com.google.common.collect.ImmutableList; + /** * Default implementation of the {@code AccessControlConfiguration}. */ @Component() @Service({AuthorizationConfiguration.class, SecurityConfiguration.class}) -public class AuthorizationConfigurationImpl extends ConfigurationBase implements AuthorizationConfiguration { +public class AuthorizationConfigurationImpl extends ConfigurationBase implements AuthorizationConfiguration { + + private final PermissionEntryCache permissionEntryCache = new PermissionEntryCache(); public AuthorizationConfigurationImpl() { super(); @@ -91,7 +94,7 @@ public class AuthorizationConfiguration public List getCommitHooks(String workspaceName) { return ImmutableList.of( new VersionablePathHook(workspaceName), - new PermissionHook(workspaceName, getRestrictionProvider())); + new PermissionHook(workspaceName, getRestrictionProvider(), permissionEntryCache)); } @Override @@ -129,6 +132,7 @@ public class AuthorizationConfiguration @Nonnull @Override public PermissionProvider getPermissionProvider(Root root, Set principals) { - return new PermissionProviderImpl(root, principals, this); + return new PermissionProviderImpl(root, principals, this, permissionEntryCache.createLocalCache()); } + } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java Fri Nov 1 00:59:06 2013 @@ -20,17 +20,16 @@ import java.security.Principal; import java.security.acl.Group; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; + import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterators; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Tree; @@ -55,6 +54,9 @@ import org.apache.jackrabbit.oak.util.Tr import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterators; + import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterators.concat; @@ -71,60 +73,55 @@ final class CompiledPermissionImpl imple Permissions.READ_PROPERTY, PrivilegeBits.BUILT_IN.get(PrivilegeConstants.REP_READ_PROPERTIES), Permissions.READ_ACCESS_CONTROL, PrivilegeBits.BUILT_IN.get(PrivilegeConstants.JCR_READ_ACCESS_CONTROL)); - private final Set principals; private ImmutableRoot root; - private final String workspaceName; - private final Map userTrees; - private final Map groupTrees; + private final String workspaceName; private final ReadPolicy readPolicy; - private final PermissionStore userStore; - private final PermissionStore groupStore; + private PermissionStoreImpl store; + private final PermissionEntryProvider userStore; + private final PermissionEntryProvider groupStore; private PrivilegeBitsProvider bitsProvider; - private CompiledPermissionImpl(@Nonnull Set principals, + private CompiledPermissionImpl(@Nonnull PermissionEntryCache.Local cache, + @Nonnull Set principals, @Nonnull ImmutableRoot root, @Nonnull String workspaceName, - @Nonnull ImmutableTree permissionsTree, @Nonnull RestrictionProvider restrictionProvider, @Nonnull Set readPaths) { - this.principals = principals; this.root = root; this.workspaceName = workspaceName; bitsProvider = new PrivilegeBitsProvider(root); readPolicy = (readPaths.isEmpty()) ? EmptyReadPolicy.INSTANCE : new DefaultReadPolicy(readPaths); - userTrees = new HashMap(principals.size()); - groupTrees = new HashMap(principals.size()); - if (permissionsTree.exists()) { - for (Principal principal : principals) { - Tree t = PermissionUtil.getPrincipalRoot(permissionsTree, principal); - Map target = (principal instanceof Group) ? groupTrees : userTrees; - if (t.exists()) { - target.put(principal.getName(), t); - } else { - target.remove(principal.getName()); - } + // setup + store = new PermissionStoreImpl(root, workspaceName, restrictionProvider); + Set userNames = new HashSet(principals.size()); + Set groupNames = new HashSet(principals.size()); + for (Principal principal : principals) { + if (principal instanceof Group) { + groupNames.add(principal.getName()); + } else { + userNames.add(principal.getName()); } } - userStore = PermissionStore.create(userTrees, restrictionProvider); - groupStore = PermissionStore.create(groupTrees, restrictionProvider); + userStore = new PermissionEntryProviderImpl(store, cache, userNames); + groupStore = new PermissionEntryProviderImpl(store, cache, groupNames); } static CompiledPermissions create(@Nonnull ImmutableRoot root, @Nonnull String workspaceName, @Nonnull Set principals, - @Nonnull AuthorizationConfiguration acConfig) { - ImmutableTree permissionsTree = PermissionUtil.getPermissionsRoot(root, workspaceName); + @Nonnull AuthorizationConfiguration acConfig, + @Nonnull PermissionEntryCache.Local cache) { + Tree permissionsTree = PermissionUtil.getPermissionsRoot(root, workspaceName); if (!permissionsTree.exists() || principals.isEmpty()) { return NoPermissions.getInstance(); } else { Set readPaths = acConfig.getParameters().getConfigValue(PARAM_READ_PATHS, DEFAULT_READ_PATHS); - return new CompiledPermissionImpl(principals, root, workspaceName, - permissionsTree, acConfig.getRestrictionProvider(), readPaths); + return new CompiledPermissionImpl(cache, principals, root, workspaceName, acConfig.getRestrictionProvider(), readPaths); } } @@ -133,40 +130,9 @@ final class CompiledPermissionImpl imple public void refresh(@Nonnull ImmutableRoot root, @Nonnull String workspaceName) { this.root = root; this.bitsProvider = new PrivilegeBitsProvider(root); - // test if a permission has been added for those principals that didn't have one before - - ImmutableTree permissionsTree = PermissionUtil.getPermissionsRoot(root, workspaceName); - boolean flushUserStore = false; - boolean flushGroupStore = false; - for (Principal principal : principals) { - boolean isGroup = (principal instanceof Group); - Map target = isGroup ? groupTrees : userTrees; - Tree principalRoot = PermissionUtil.getPrincipalRoot(permissionsTree, principal); - String pName = principal.getName(); - - if (principalRoot.exists()) { - if (!target.containsKey(pName) || !principalRoot.equals(target.get(pName))) { - target.put(pName, principalRoot); - if (isGroup) { - flushGroupStore = true; - } else { - flushUserStore = true; - } - } - } else if (target.remove(pName) != null) { - if (isGroup) { - flushGroupStore = true; - } else { - flushUserStore = true; - } - } - } - if (flushUserStore) { - userStore.flush(); - } - if (flushGroupStore) { - groupStore.flush(); - } + store.flush(root); + userStore.flush(); + groupStore.flush(); } @Override @@ -219,7 +185,7 @@ final class CompiledPermissionImpl imple @Nonnull private TreePermission getParentPermission(@Nonnull Tree tree, int type) { - List trees = new ArrayList(); + List trees = new ArrayList(); while (!tree.isRoot()) { tree = tree.getParent(); if (tree.exists()) { Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntry.java Fri Nov 1 00:59:06 2013 @@ -111,9 +111,9 @@ final class PermissionEntry implements C return -1; } } else { - int depth = PathUtils.getDepth(path); - int otherDepth = PathUtils.getDepth(pe.path); - if (PathUtils.getDepth(path) == otherDepth) { + final int depth = PathUtils.getDepth(path); + final int otherDepth = PathUtils.getDepth(pe.path); + if (depth == otherDepth) { return path.compareTo(pe.path); } else { return (depth < otherDepth) ? 1 : -1; Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryCache.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryCache.java?rev=1537792&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryCache.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryCache.java Fri Nov 1 00:59:06 2013 @@ -0,0 +1,90 @@ +/* + * 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.authorization.permission; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * PermissionEntryCache caches the permission entries of principals. The cache is held globally and contains + * a version of the principal permission entries of the session that read them last. each session gets a lazy copy of + * the cache and needs to verify if each cached principal permission set still reflects the state that the session sees. + * every newly loaded principal permission set can be pushed down to the base cache if it does not exist there yet, or + * if it's newer. + */ +public class PermissionEntryCache { + + private final Map base = new ConcurrentHashMap(); + + public Local createLocalCache() { + return new Local(); + } + + public void flush(Set principalNames) { + base.keySet().removeAll(principalNames); + } + + public class Local { + + private final Map entries = new HashMap(); + + private final Set verified = new HashSet(); + + public Local() { + entries.putAll(base); + } + + public PrincipalPermissionEntries getEntries(PermissionStore store, String principalName) { + PrincipalPermissionEntries ppe = entries.get(principalName); + if (ppe == null) { + ppe = store.load(principalName); + entries.put(principalName, ppe); + } else { + if (!verified.contains(principalName)) { + if (store.getTimestamp(principalName) != ppe.getTimestamp()) { + ppe = store.load(principalName); + entries.put(principalName, ppe); + } + verified.add(principalName); + } + } + + // check if base cache has the entries + PrincipalPermissionEntries baseppe = base.get(principalName); + if (baseppe == null || ppe.getTimestamp() > baseppe.getTimestamp()) { + base.put(principalName, ppe); + } + return ppe; + } + + public boolean hasEntries(PermissionStore store, String principalName) { + return getNumEntries(store, principalName) > 0; + } + + public long getNumEntries(PermissionStore store, String principalName) { + return getEntries(store, principalName).getEntries().size(); + } + + public void flush(Set principalNames) { + verified.removeAll(principalNames); + } + + } +} \ No newline at end of file Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProvider.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProvider.java?rev=1537792&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProvider.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProvider.java Fri Nov 1 00:59:06 2013 @@ -0,0 +1,40 @@ +/************************************************************************* + * + * ADOBE CONFIDENTIAL + * ___________________ + * + * Copyright ${today.year} Adobe Systems Incorporated + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe Systems Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Adobe Systems Incorporated and its + * suppliers and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe Systems Incorporated. + **************************************************************************/ +package org.apache.jackrabbit.oak.security.authorization.permission; + +import java.util.Collection; +import java.util.Iterator; + +import javax.annotation.Nonnull; + +import org.apache.jackrabbit.oak.api.Tree; + +/** + * PermissionEntryProvider provides permission entries for a given set of principals. + * It may internally hold a cache to improve performance and usually operates on the permission store. + */ +public interface PermissionEntryProvider { + + Iterator getEntryIterator(@Nonnull EntryPredicate predicate); + + Collection getEntries(@Nonnull Tree accessControlledTree); + + Collection getEntries(@Nonnull String accessControlledPath); + + void flush(); +} \ No newline at end of file Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImpl.java?rev=1537792&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImpl.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionEntryProviderImpl.java Fri Nov 1 00:59:06 2013 @@ -0,0 +1,180 @@ +/* + * 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.authorization.permission; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; + +import com.google.common.base.Strings; +import com.google.common.collect.Iterators; + +/** + * PermissionEntryProviderImpl... + */ +public class PermissionEntryProviderImpl implements PermissionEntryProvider { + + private static final long MAX_SIZE = 250; // TODO define size or make configurable + + private final Set principalNames; + + private final Set existingNames = new HashSet(); + + private Map> pathEntryMap; + + private final PermissionStore store; + + private final PermissionEntryCache.Local cache; + + protected PermissionEntryProviderImpl(@Nonnull PermissionStore store, @Nonnull PermissionEntryCache.Local cache, + @Nonnull Set principalNames) { + this.store = store; + this.cache = cache; + this.principalNames = Collections.unmodifiableSet(principalNames); + init(); + } + + private void init() { + long cnt = 0; + existingNames.clear(); + for (String name: principalNames) { + if (cnt > MAX_SIZE) { + if (cache.hasEntries(store, name)) { + existingNames.add(name); + } + } else { + long n = cache.getNumEntries(store, name); + cnt+= n; + if (n > 0) { + existingNames.add(name); + } + } + } + this.pathEntryMap = cnt < MAX_SIZE + ? createMap(cache, store, existingNames) + : null; + } + + public void flush() { + cache.flush(principalNames); + init(); + } + + public Iterator getEntryIterator(@Nonnull EntryPredicate predicate) { + if (existingNames.isEmpty()) { + return Iterators.emptyIterator(); + } else { + return new EntryIterator(predicate); + } + } + + public Collection getEntries(@Nonnull Tree accessControlledTree) { + if (existingNames.isEmpty()) { + return Collections.emptyList(); + } else if (pathEntryMap != null) { + Collection entries = pathEntryMap.get(accessControlledTree.getPath()); + return (entries != null) ? entries : Collections.emptyList(); + } else { + return (accessControlledTree.hasChild(AccessControlConstants.REP_POLICY)) ? + loadEntries(accessControlledTree.getPath()) : + Collections.emptyList(); + } + } + + @Nonnull + public Collection getEntries(@Nonnull String path) { + if (existingNames.isEmpty()) { + return Collections.emptyList(); + } else if (pathEntryMap != null) { + Collection entries = pathEntryMap.get(path); + return (entries != null) ? entries : Collections.emptyList(); + } else { + return loadEntries(path); + } + } + + @Nonnull + private Collection loadEntries(@Nonnull String path) { + Collection ret = new TreeSet(); + for (String name: existingNames) { + PrincipalPermissionEntries ppe = cache.getEntries(store, name); + ret.addAll(ppe.getEntries(path)); + } + return ret; + } + + private static Map> createMap(@Nonnull PermissionEntryCache.Local cache, + @Nonnull PermissionStore store, + @Nonnull Set principalNames) { + Map> pathEntryMap = new HashMap>(); + for (String name: principalNames) { + PrincipalPermissionEntries ppe = cache.getEntries(store, name); + for (Map.Entry> e: ppe.getEntries().entrySet()) { + Collection pathEntries = pathEntryMap.get(e.getKey()); + if (pathEntries == null) { + pathEntries = new TreeSet(e.getValue()); + pathEntryMap.put(e.getKey(), pathEntries); + } else { + pathEntries.addAll(e.getValue()); + } + } + } + return pathEntryMap; + } + + private final class EntryIterator extends AbstractEntryIterator { + + private final EntryPredicate predicate; + + // the next oak path for which to retrieve permission entries + private String path; + + private EntryIterator(@Nonnull EntryPredicate predicate) { + this.predicate = predicate; + this.path = Strings.nullToEmpty(predicate.getPath()); + } + + @CheckForNull + protected void seekNext() { + for (next = null; next == null; ) { + if (nextEntries.hasNext()) { + PermissionEntry pe = nextEntries.next(); + if (predicate.apply(pe)) { + next = pe; + } + } else { + if (path == null) { + break; + } + nextEntries = getEntries(path).iterator(); + path = PermissionUtil.getParentPathOrNull(path); + } + } + } + } +} \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHook.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHook.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHook.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionHook.java Fri Nov 1 00:59:06 2013 @@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.securi import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -83,6 +84,7 @@ public class PermissionHook implements P private final RestrictionProvider restrictionProvider; private final String workspaceName; + private final PermissionEntryCache cache; private NodeBuilder permissionRoot; private ReadOnlyNodeTypeManager ntMgr; @@ -91,9 +93,10 @@ public class PermissionHook implements P private Map modified = new HashMap(); private Map deleted = new HashMap(); - public PermissionHook(String workspaceName, RestrictionProvider restrictionProvider) { + public PermissionHook(String workspaceName, RestrictionProvider restrictionProvider, PermissionEntryCache cache) { this.workspaceName = workspaceName; this.restrictionProvider = restrictionProvider; + this.cache = cache; } @Nonnull @@ -112,12 +115,14 @@ public class PermissionHook implements P } private void apply() { + Set principalNames = new HashSet(); for (Map.Entry entry : deleted.entrySet()) { - entry.getValue().remove(); + entry.getValue().remove(principalNames); } for (Map.Entry entry : modified.entrySet()) { - entry.getValue().update(); + entry.getValue().update(principalNames); } + cache.flush(principalNames); } private boolean isACL(@Nonnull Tree tree) { @@ -291,10 +296,11 @@ public class PermissionHook implements P } } - private void remove() { + private void remove(Set principalNames) { String msg = "Unable to remove permission entry"; for (String principalName: entries.keySet()) { if (permissionRoot.hasChildNode(principalName)) { + principalNames.add(principalName); NodeBuilder principalRoot = permissionRoot.getChildNode(principalName); // find the ACL node that for this path and principal @@ -343,14 +349,16 @@ public class PermissionHook implements P } } principalRoot.setProperty(REP_NUM_PERMISSIONS, numEntries); + principalRoot.setProperty(REP_TIMESTAMP, System.currentTimeMillis()); } else { log.error("{} {}: Principal root missing.", msg, this); } } } - private void update() { + private void update(Set principalNames) { for (String principalName: entries.keySet()) { + principalNames.add(principalName); NodeBuilder principalRoot = permissionRoot.child(principalName); if (!principalRoot.hasProperty(JCR_PRIMARYTYPE)) { principalRoot.setProperty(JCR_PRIMARYTYPE, NT_REP_PERMISSION_STORE, Type.NAME); @@ -397,6 +405,7 @@ public class PermissionHook implements P long numEntries = PermissionUtil.getNumPermissions(principalRoot); numEntries+= updateEntries(parent, entries.get(principalName)); principalRoot.setProperty(REP_NUM_PERMISSIONS, numEntries); + principalRoot.setProperty(REP_TIMESTAMP, System.currentTimeMillis()); } } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImpl.java Fri Nov 1 00:59:06 2013 @@ -58,7 +58,8 @@ public class PermissionProviderImpl impl private ImmutableRoot immutableRoot; public PermissionProviderImpl(@Nonnull Root root, @Nonnull Set principals, - @Nonnull AuthorizationConfiguration acConfig) { + @Nonnull AuthorizationConfiguration acConfig, + @Nonnull PermissionEntryCache.Local cache) { this.root = root; this.workspaceName = root.getContentSession().getWorkspaceName(); this.acConfig = acConfig; @@ -68,7 +69,7 @@ public class PermissionProviderImpl impl if (principals.contains(SystemPrincipal.INSTANCE) || isAdmin(principals)) { compiledPermissions = AllPermissions.getInstance(); } else { - compiledPermissions = CompiledPermissionImpl.create(immutableRoot, workspaceName, principals, acConfig); + compiledPermissions = CompiledPermissionImpl.create(immutableRoot, workspaceName, principals, acConfig, cache); } } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStore.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStore.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStore.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStore.java Fri Nov 1 00:59:06 2013 @@ -17,175 +17,26 @@ package org.apache.jackrabbit.oak.security.authorization.permission; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; import java.util.Map; -import java.util.TreeSet; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; -import com.google.common.base.Strings; -import com.google.common.collect.Iterators; -import org.apache.jackrabbit.oak.api.Tree; -import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; -import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; -import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; -import org.apache.jackrabbit.oak.util.TreeUtil; +import javax.annotation.Nonnull; /** - * The Permission store reads the principal based access control permissions. - * One store is currently used to handle 1 set of principal trees (so the compiled - * permissions use 2 stores, one for the user principals and one for the - * group principals). + * The permission store is used to store and provide access control permissions for principals. It is responsible to + * load and store the permissions in an optimal form in the repository and must not cache them. */ -final class PermissionStore implements PermissionConstants { +public interface PermissionStore { - private static final long MAX_SIZE = 250; // TODO define size or make configurable + void load(@Nonnull Collection entries, @Nonnull String principalName, @Nonnull String path); - private final Map principalTrees; - private final RestrictionProvider restrictionProvider; - private Map> pathEntryMap; - - private PermissionStore(@Nonnull Map principalTrees, - @Nonnull RestrictionProvider restrictionProvider, - boolean doCreateMap) { - this.principalTrees = principalTrees; - this.restrictionProvider = restrictionProvider; - this.pathEntryMap = (doCreateMap) ? - createMap(principalTrees.values(), restrictionProvider) : null; - } - - static PermissionStore create(@Nonnull Map principalTrees, - @Nonnull RestrictionProvider restrictionProvider) { - long cnt = 0; - if (!principalTrees.isEmpty()) { - Iterator treeItr = principalTrees.values().iterator(); - while (treeItr.hasNext() && cnt < MAX_SIZE) { - cnt += PermissionUtil.getNumPermissions(treeItr.next()); - } - } - return new PermissionStore(principalTrees, restrictionProvider, (cnt < MAX_SIZE)); - } - - public void flush() { - if (pathEntryMap != null) { - pathEntryMap = createMap(principalTrees.values(), restrictionProvider); - } - } - - public Iterator getEntryIterator(@Nonnull EntryPredicate predicate) { - if (principalTrees.isEmpty()) { - return Iterators.emptyIterator(); - } else { - return new EntryIterator(predicate); - } - } - - public Collection getEntries(@Nonnull Tree tree) { - if (principalTrees.isEmpty()) { - return Collections.emptyList(); - } else if (pathEntryMap != null) { - Collection entries = pathEntryMap.get(tree.getPath()); - return (entries != null) ? entries : Collections.emptyList(); - } else { - return (tree.hasChild(AccessControlConstants.REP_POLICY)) ? - getEntries(tree.getPath()) : - Collections.emptyList(); - } - } + void load(@Nonnull Map> entries, @Nonnull String principalName); @Nonnull - private Collection getEntries(@Nonnull String path) { - Collection ret = new TreeSet(); - String name = PermissionUtil.getEntryName(path); - for (Map.Entry principalRoot : principalTrees.entrySet()) { - Tree parent = principalRoot.getValue(); - if (parent.hasChild(name)) { - Tree child = parent.getChild(name); - if (PermissionUtil.checkACLPath(child, path)) { - loadPermissionEntries(path, ret, child, restrictionProvider); - } else { - // check for child node - for (Tree node : child.getChildren()) { - if (PermissionUtil.checkACLPath(node, path)) { - loadPermissionEntries(path, ret, node, restrictionProvider); - } - } - } - } - } - return ret; - } - - private static Map> createMap(@Nonnull Collection principalTrees, - @Nonnull RestrictionProvider restrictionProvider) { - Map> pathEntryMap = new HashMap>(); - for (Tree principalTree : principalTrees) { - for (Tree entryTree : principalTree.getChildren()) { - loadPermissionEntries(entryTree, pathEntryMap, restrictionProvider); - } - } - return pathEntryMap; - } - - private static void loadPermissionEntries(@Nonnull Tree tree, - @Nonnull Map> pathEntryMap, - @Nonnull RestrictionProvider restrictionProvider) { - String path = TreeUtil.getString(tree, REP_ACCESS_CONTROLLED_PATH); - Collection entries = pathEntryMap.get(path); - if (entries == null) { - entries = new TreeSet(); - pathEntryMap.put(path, entries); - } - for (Tree child : tree.getChildren()) { - if (child.getName().charAt(0) == 'c') { - loadPermissionEntries(child, pathEntryMap, restrictionProvider); - } else { - entries.add(new PermissionEntry(path, child, restrictionProvider)); - } - } - } - - private static void loadPermissionEntries(@Nonnull String path, - @Nonnull Collection ret, - @Nonnull Tree tree, - @Nonnull RestrictionProvider restrictionProvider) { - for (Tree ace : tree.getChildren()) { - if (ace.getName().charAt(0) != 'c') { - ret.add(new PermissionEntry(path, ace, restrictionProvider)); - } - } - } - - private final class EntryIterator extends AbstractEntryIterator { - - private final EntryPredicate predicate; - - // the next oak path for which to retrieve permission entries - private String path; - - private EntryIterator(@Nonnull EntryPredicate predicate) { - this.predicate = predicate; - this.path = Strings.nullToEmpty(predicate.getPath()); - } - - @CheckForNull - protected void seekNext() { - for (next = null; next == null; ) { - if (nextEntries.hasNext()) { - PermissionEntry pe = nextEntries.next(); - if (predicate.apply(pe)) { - next = pe; - } - } else { - if (path == null) { - break; - } - nextEntries = getEntries(path).iterator(); - path = PermissionUtil.getParentPathOrNull(path); - } - } - } - } + PrincipalPermissionEntries load(@Nonnull String principalName); + + boolean hasPermissionEntries(@Nonnull String principalName); + + long getNumEntries(@Nonnull String principalName); + + long getTimestamp(@Nonnull String principalName); } Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImpl.java?rev=1537792&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImpl.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreImpl.java Fri Nov 1 00:59:06 2013 @@ -0,0 +1,171 @@ +/* + * 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.authorization.permission; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; +import org.apache.jackrabbit.oak.util.TreeUtil; + +/** + * PermissionStoreImpl... + */ +public class PermissionStoreImpl implements PermissionStore { + + private Tree permissionsTree; + + private final String workspaceName; + + private final RestrictionProvider restrictionProvider; + + private final Map principalTreeMap = new HashMap(); + + public PermissionStoreImpl(Root root, String workspaceName, RestrictionProvider restrictionProvider) { + this.permissionsTree = PermissionUtil.getPermissionsRoot(root, workspaceName); + this.workspaceName = workspaceName; + this.restrictionProvider = restrictionProvider; + } + + protected void flush(Root root) { + this.permissionsTree = PermissionUtil.getPermissionsRoot(root, workspaceName); + this.principalTreeMap.clear(); + } + + @CheckForNull + private Tree getPrincipalRoot(@Nonnull String principalName) { + if (principalTreeMap.containsKey(principalName)) { + return principalTreeMap.get(principalName); + } else { + Tree principalRoot = PermissionUtil.getPrincipalRoot(permissionsTree, principalName); + if (!principalRoot.exists()) { + principalRoot = null; + } + principalTreeMap.put(principalName, principalRoot); + return principalRoot; + } + } + + @Override + public void load(@Nonnull Collection entries, @Nonnull String principalName, @Nonnull String path) { + Tree principalRoot = getPrincipalRoot(principalName); + if (principalRoot == null) { + return; + } + String name = PermissionUtil.getEntryName(path); + if (principalRoot.hasChild(name)) { + Tree child = principalRoot.getChild(name); + if (PermissionUtil.checkACLPath(child, path)) { + loadPermissionEntries(path, entries, child, restrictionProvider); + } else { + // check for child node + for (Tree node : child.getChildren()) { + if (PermissionUtil.checkACLPath(node, path)) { + loadPermissionEntries(path, entries, node, restrictionProvider); + } + } + } + } + } + + @Override + public void load(@Nonnull Map> entries, @Nonnull String principalName) { + Tree principalRoot = getPrincipalRoot(principalName); + if (principalRoot != null) { + for (Tree entryTree : principalRoot.getChildren()) { + loadPermissionEntries(entryTree, entries, restrictionProvider); + } + } + } + + @Override + public boolean hasPermissionEntries(@Nonnull String principalName) { + return getPrincipalRoot(principalName) != null; + } + + @Override + public long getNumEntries(@Nonnull String principalName) { + Tree tree = getPrincipalRoot(principalName); + return tree == null ? 0 : PermissionUtil.getNumPermissions(tree); + } + + @Override + public long getTimestamp(@Nonnull String principalName) { + Tree principalRoot = getPrincipalRoot(principalName); + if (principalRoot != null) { + PropertyState ps = principalRoot.getProperty(PermissionConstants.REP_TIMESTAMP); + if (ps != null) { + return ps.getValue(Type.LONG); + } + } + return 0; + } + + @Override + @Nonnull + public PrincipalPermissionEntries load(@Nonnull String principalName) { + PrincipalPermissionEntries ret = new PrincipalPermissionEntries(principalName); + Tree principalRoot = getPrincipalRoot(principalName); + if (principalRoot != null) { + for (Tree entryTree : principalRoot.getChildren()) { + loadPermissionEntries(entryTree, ret.getEntries(), restrictionProvider); + } + PropertyState ps = principalRoot.getProperty(PermissionConstants.REP_TIMESTAMP); + ret.setTimestamp(ps == null ? System.currentTimeMillis() : ps.getValue(Type.LONG)); + } + return ret; + } + + private static void loadPermissionEntries(@Nonnull Tree tree, + @Nonnull Map> pathEntryMap, + @Nonnull RestrictionProvider restrictionProvider) { + String path = TreeUtil.getString(tree, PermissionConstants.REP_ACCESS_CONTROLLED_PATH); + Collection entries = pathEntryMap.get(path); + if (entries == null) { + entries = new TreeSet(); + pathEntryMap.put(path, entries); + } + for (Tree child : tree.getChildren()) { + if (child.getName().charAt(0) == 'c') { + loadPermissionEntries(child, pathEntryMap, restrictionProvider); + } else { + entries.add(new PermissionEntry(path, child, restrictionProvider)); + } + } + } + + private static void loadPermissionEntries(@Nonnull String path, + @Nonnull Collection ret, + @Nonnull Tree tree, + @Nonnull RestrictionProvider restrictionProvider) { + for (Tree ace : tree.getChildren()) { + if (ace.getName().charAt(0) != 'c') { + ret.add(new PermissionEntry(path, ace, restrictionProvider)); + } + } + } +} \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionUtil.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionUtil.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionUtil.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionUtil.java Fri Nov 1 00:59:06 2013 @@ -22,6 +22,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.PathUtils; @@ -86,13 +87,13 @@ public final class PermissionUtil implem } @Nonnull - public static ImmutableTree getPermissionsRoot(ImmutableRoot immutableRoot, String workspaceName) { - return immutableRoot.getTree(PERMISSIONS_STORE_PATH + '/' + workspaceName); + public static Tree getPermissionsRoot(Root root, String workspaceName) { + return root.getTree(PERMISSIONS_STORE_PATH + '/' + workspaceName); } @Nonnull - public static Tree getPrincipalRoot(Tree permissionsTree, Principal principal) { - return permissionsTree.getChild(Text.escapeIllegalJcrChars(principal.getName())); + public static Tree getPrincipalRoot(Tree permissionsTree, String principalName) { + return permissionsTree.getChild(Text.escapeIllegalJcrChars(principalName)); } public static int getType(@Nonnull ImmutableTree tree, @Nullable PropertyState property) { Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntries.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntries.java?rev=1537792&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntries.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/PrincipalPermissionEntries.java Fri Nov 1 00:59:06 2013 @@ -0,0 +1,82 @@ +/* + * 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.authorization.permission; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; +import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; +import org.apache.jackrabbit.oak.util.TreeUtil; + +/** + * PermissionEntries holds the permission entries of one principal + */ +public class PrincipalPermissionEntries { + + /** + * principal name + */ + private final String name; + + /** + * timestamp of the per-principal permission store + */ + private long timestamp; + + /** + * map of permission entries, accessed by path + */ + private Map> entries = new HashMap>(); + + public PrincipalPermissionEntries(@Nonnull String name) { + this.name = name; + } + + @Nonnull + public String getName() { + return name; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + @Nonnull + public Collection getEntries(@Nonnull String path) { + Collection ret = entries.get(path); + return ret == null ? Collections.emptyList() : ret; + } + + @Nonnull + public Map> getEntries() { + return entries; + } + +} \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/PermissionConstants.java Fri Nov 1 00:59:06 2013 @@ -38,6 +38,7 @@ public interface PermissionConstants { String REP_ACCESS_CONTROLLED_PATH = "rep:accessControlledPath"; String REP_NUM_PERMISSIONS = "rep:numPermissions"; + String REP_TIMESTAMP = "rep:timestamp"; String REP_IS_ALLOW = "rep:isAllow"; String REP_PRIVILEGE_BITS = "rep:privileges"; Modified: jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd (original) +++ jackrabbit/oak/trunk/oak-core/src/main/resources/org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd Fri Nov 1 00:59:06 2013 @@ -671,6 +671,7 @@ [rep:PermissionStore] - rep:accessControlledPath (STRING) protected - rep:numPermissions (LONG) protected + - rep:timestamp (LONG) protected + * (rep:PermissionStore) = rep:PermissionStore protected IGNORE + * (rep:Permissions) = rep:Permissions protected IGNORE Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/AbstractOakCoreTest.java Fri Nov 1 00:59:06 2013 @@ -20,6 +20,7 @@ import static com.google.common.base.Pre import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED; import java.security.Principal; +import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -116,11 +117,27 @@ public abstract class AbstractOakCoreTes @Nonnull Principal principal, boolean isAllow, @Nonnull String... privilegeNames) throws Exception { + setupPermission(root, path, principal, isAllow, privilegeNames); + } + + /** + * Setup simple allow/deny permissions (without restrictions). + * + * @param path + * @param principal + * @param isAllow + * @param privilegeNames + * @throws Exception + */ + protected void setupPermission(@Nonnull Root root, + @Nullable String path, + @Nonnull Principal principal, + boolean isAllow, + @Nonnull String... privilegeNames) throws Exception { AccessControlManager acMgr = getAccessControlManager(root); JackrabbitAccessControlList acl = checkNotNull(AccessControlUtils.getAccessControlList(acMgr, path)); acl.addEntry(principal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), isAllow); acMgr.setPolicy(path, acl); - root.commit(); } } \ No newline at end of file Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/MultipleSessionsACLStabilityTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/MultipleSessionsACLStabilityTest.java?rev=1537792&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/MultipleSessionsACLStabilityTest.java (added) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/evaluation/MultipleSessionsACLStabilityTest.java Fri Nov 1 00:59:06 2013 @@ -0,0 +1,124 @@ +/* + * 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.authorization.evaluation; + +import java.security.Principal; +import java.util.List; +import java.util.UUID; + +import javax.jcr.SimpleCredentials; +import javax.jcr.security.AccessControlManager; + +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; +import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; +import org.apache.jackrabbit.oak.api.ContentSession; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; +import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests if ACL changes done by different sessions are isolated correctly following the MVCC pattern. + */ +public class MultipleSessionsACLStabilityTest extends AbstractOakCoreTest { + + private Root testRoot1; + + private Root testRoot2; + + private ContentSession testSession1; + + private ContentSession testSession2; + + @Before + public void before() throws Exception { + super.before(); + + testSession1 = getTestSession(); + String uid = testPrincipal.getName(); + testSession2 = login(new SimpleCredentials(uid, uid.toCharArray())); + + setupPermission("/", testPrincipal, true, PrivilegeConstants.JCR_ALL); + setupPermission("/a/bb", testPrincipal, false, PrivilegeConstants.JCR_READ); + + testRoot1 = getTestRoot(); + testRoot2 = testSession2.getLatestRoot(); + } + + + @Test + public void testAllowChild() throws Exception { + Tree rootTree1 = testRoot1.getTree("/"); + Tree rootTree2 = testRoot2.getTree("/"); + + assertFalse(rootTree1.hasChild("a/bb")); + assertFalse(rootTree2.hasChild("a/bb")); + + // now allow read with root session + setupPermission("/a/bb", testPrincipal, true, PrivilegeConstants.JCR_READ); + + // the test sessions still need to see the old ACLs + assertFalse(rootTree1.hasChild("a/bb")); + assertFalse(rootTree2.hasChild("a/bb")); + } + + @Test + public void testAllowChild2() throws Exception { + // same as above, but before reading the items + setupPermission("/a/bb", testPrincipal, true, PrivilegeConstants.JCR_READ); + + Tree rootTree1 = testRoot1.getTree("/"); + Tree rootTree2 = testRoot2.getTree("/"); + + assertFalse(rootTree1.hasChild("a/bb")); + assertFalse(rootTree2.hasChild("a/bb")); + } + + @Test + public void testAllowChild3() throws Exception { + Tree rootTree1 = testRoot1.getTree("/"); + Tree rootTree2 = testRoot2.getTree("/"); + + assertTrue(rootTree1.hasChild("a")); + assertTrue(rootTree2.hasChild("a")); + assertFalse(rootTree1.hasChild("a/bb")); + assertFalse(rootTree2.hasChild("a/bb")); + + setupPermission(testRoot1, "/a", testPrincipal, false, PrivilegeConstants.JCR_READ); + + assertFalse(rootTree1.hasChild("a")); + assertTrue(rootTree2.hasChild("a")); + + String uid = testPrincipal.getName(); + ContentSession session3 = login(new SimpleCredentials(uid, uid.toCharArray())); + Tree rootTree3 = session3.getLatestRoot().getTree("/"); + assertFalse(rootTree3.hasChild("a")); + + } +} Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImplTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImplTest.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImplTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionProviderImplTest.java Fri Nov 1 00:59:06 2013 @@ -102,7 +102,7 @@ public class PermissionProviderImplTest } private PermissionProvider createPermissionProvider(ContentSession session) { - return new PermissionProviderImpl(session.getLatestRoot(), session.getAuthInfo().getPrincipals(), config); + return config.getPermissionProvider(session.getLatestRoot(), session.getAuthInfo().getPrincipals()); } @Test Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreTest.java?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/PermissionStoreTest.java Fri Nov 1 00:59:06 2013 @@ -87,8 +87,8 @@ public class PermissionStoreTest extends } } - private PermissionProviderImpl createPermissionProvider() { - return new PermissionProviderImpl(testRoot, testSession.getAuthInfo().getPrincipals(), acConfig); + private PermissionProvider createPermissionProvider() { + return acConfig.getPermissionProvider(testRoot, testSession.getAuthInfo().getPrincipals()); } @Test Modified: jackrabbit/oak/trunk/oak-run/run_concurrent.sh URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/run_concurrent.sh?rev=1537792&r1=1537791&r2=1537792&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-run/run_concurrent.sh (original) +++ jackrabbit/oak/trunk/oak-run/run_concurrent.sh Fri Nov 1 00:59:06 2013 @@ -16,19 +16,21 @@ # limitations under the License. # USERS="false true" -RUNTIME=5 +RUNTIME=10 #BENCH=ConcurrentReadAccessControlledTreeTest BENCH=ManyUserReadTest RANDOM_USER="true" FIXS="Oak-Tar Jackrabbit" THREADS="1,2,4,8,10,15,20,50" PROFILE=false +NUM_ITEMS=10000 LOG=$BENCH"_$(date +'%Y%m%d_%H%M%S').csv" echo "Benchmarks: $BENCH" > $LOG echo "Fixtures: $FIXS" >> $LOG echo "Users: $USERS" >> $LOG echo "Runtime: $RUNTIME" >> $LOG +echo "Num Items: $NUM_ITEMS" >> $LOG echo "Concurrency: $THREADS" >> $LOG echo "Random User: $RANDOM_USER" >> $LOG echo "Profiling: $PROFILE" >> $LOG @@ -41,7 +43,7 @@ for user in $USERS echo "Executing benchmarks as admin: $user on $fix" | tee -a $LOG echo "-----------------------------------------------------------" | tee -a $LOG rm -rf target/Jackrabbit-* target/Oak-Tar-* - cmd="java -Xmx2048m -Dprofile=$PROFILE -Druntime=$RUNTIME -jar target/oak-run-*-SNAPSHOT.jar benchmark --csvFile $LOG --concurrency $THREADS --runAsAdmin $user --report false --randomUser $RANDOM_USER $BENCH $fix" + cmd="java -Xmx2048m -Dprofile=$PROFILE -Druntime=$RUNTIME -Dwarmup=20 -jar target/oak-run-*-SNAPSHOT.jar benchmark --itemsToRead $NUM_ITEMS --csvFile $LOG --concurrency $THREADS --runAsAdmin $user --report false --randomUser $RANDOM_USER $BENCH $fix" echo $cmd $cmd done