+
+ Tracker<MountInfoProvider> tracker =
whiteboard.track(MountInfoProvider.class);
+ List<MountInfoProvider> services = tracker.getServices();
+ tracker.stop();
+
+ if ( services.isEmpty() )
+ this.mountInfoProvider = null;
+ else if ( services.size() == 1 )
+ this.mountInfoProvider = services.get(0);
+ else
+ throw new IllegalArgumentException("Found " +
services.size() + " MountInfoProvider references, expected at most
1.");
You can also use WhiteboardUtils#getService here which hides this stuff
Chetan Mehrotra
On Tue, Aug 29, 2017 at 7:48 AM, <[email protected]> wrote:
> Author: rombert
> Date: Tue Aug 29 14:48:56 2017
> New Revision: 1806602
>
> URL: http://svn.apache.org/viewvc?rev=1806602&view=rev
> Log:
> OAK-6563 - Session.hasCapability(...) should reflect read-only status of
> mounts
>
> Add optional support for checking the Mount status in
> SessionImpl.hasCapability.
>
> Added:
>
> jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionImplCapabilityWithMountInfoProviderTest.java
> Modified:
>
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
>
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java
>
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
>
> Modified:
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
> URL:
> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java?rev=1806602&r1=1806601&r2=1806602&view=diff
> ==============================================================================
> ---
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
> (original)
> +++
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
> Tue Aug 29 14:48:56 2017
> @@ -22,6 +22,7 @@ import static java.util.Collections.sing
> import static
> org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
>
> import java.io.Closeable;
> +import java.util.List;
> import java.util.Map;
> import java.util.concurrent.Callable;
> import java.util.concurrent.ScheduledExecutorService;
> @@ -61,8 +62,10 @@ import org.apache.jackrabbit.oak.jcr.ses
> import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
> import org.apache.jackrabbit.oak.spi.gc.DelegatingGCMonitor;
> import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
> +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
> import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
> import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
> +import org.apache.jackrabbit.oak.spi.whiteboard.Tracker;
> import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
> import org.apache.jackrabbit.oak.stats.Clock;
> import org.apache.jackrabbit.oak.stats.StatisticManager;
> @@ -105,6 +108,7 @@ public class RepositoryImpl implements J
> private final Clock.Fast clock;
> private final DelegatingGCMonitor gcMonitor = new DelegatingGCMonitor();
> private final Registration gcMonitorRegistration;
> + private final MountInfoProvider mountInfoProvider;
>
> /**
> * {@link ThreadLocal} counter that keeps track of the save operations
> @@ -152,6 +156,17 @@ public class RepositoryImpl implements J
> this.clock = new Clock.Fast(scheduledExecutor);
> this.gcMonitorRegistration = whiteboard.register(GCMonitor.class,
> gcMonitor, emptyMap());
> this.fastQueryResultSize = fastQueryResultSize;
> +
> + Tracker<MountInfoProvider> tracker =
> whiteboard.track(MountInfoProvider.class);
> + List<MountInfoProvider> services = tracker.getServices();
> + tracker.stop();
> +
> + if ( services.isEmpty() )
> + this.mountInfoProvider = null;
> + else if ( services.size() == 1 )
> + this.mountInfoProvider = services.get(0);
> + else
> + throw new IllegalArgumentException("Found " + services.size() +
> " MountInfoProvider references, expected at most 1.");
> }
>
> //---------------------------------------------------------< Repository
> >---
> @@ -343,7 +358,7 @@ public class RepositoryImpl implements J
> Map<String, Object> attributes, SessionDelegate delegate, int
> observationQueueLength,
> CommitRateLimiter commitRateLimiter) {
> return new SessionContext(this, statisticManager, securityProvider,
> whiteboard, attributes,
> - delegate, observationQueueLength, commitRateLimiter,
> fastQueryResultSize);
> + delegate, observationQueueLength, commitRateLimiter,
> mountInfoProvider, fastQueryResultSize);
> }
>
> /**
>
> Modified:
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java
> URL:
> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java?rev=1806602&r1=1806601&r2=1806602&view=diff
> ==============================================================================
> ---
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java
> (original)
> +++
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java
> Tue Aug 29 14:48:56 2017
> @@ -28,6 +28,7 @@ import java.util.Set;
>
> import javax.annotation.CheckForNull;
> import javax.annotation.Nonnull;
> +import javax.annotation.Nullable;
> import javax.jcr.PathNotFoundException;
> import javax.jcr.Repository;
> import javax.jcr.RepositoryException;
> @@ -56,6 +57,7 @@ import org.apache.jackrabbit.oak.namepat
> import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
> import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
> import org.apache.jackrabbit.oak.plugins.value.jcr.ValueFactoryImpl;
> +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
> import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
> import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
> import
> org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
> @@ -89,6 +91,7 @@ public class SessionContext implements N
> private final SessionDelegate delegate;
> private final int observationQueueLength;
> private final CommitRateLimiter commitRateLimiter;
> + private MountInfoProvider mountInfoProvider;
>
> private final NamePathMapper namePathMapper;
> private final ValueFactory valueFactory;
> @@ -118,7 +121,7 @@ public class SessionContext implements N
> int observationQueueLength, CommitRateLimiter
> commitRateLimiter) {
>
> this(repository, statisticManager, securityProvider, whiteboard,
> attributes, delegate,
> - observationQueueLength, commitRateLimiter, false);
> + observationQueueLength, commitRateLimiter, null, false);
> }
>
> public SessionContext(
> @@ -126,7 +129,7 @@ public class SessionContext implements N
> @Nonnull SecurityProvider securityProvider, @Nonnull Whiteboard
> whiteboard,
> @Nonnull Map<String, Object> attributes, @Nonnull final
> SessionDelegate delegate,
> int observationQueueLength, CommitRateLimiter commitRateLimiter,
> - boolean fastQueryResultSize) {
> + MountInfoProvider mountInfoProvider, boolean
> fastQueryResultSize) {
> this.repository = checkNotNull(repository);
> this.statisticManager = statisticManager;
> this.securityProvider = checkNotNull(securityProvider);
> @@ -135,6 +138,7 @@ public class SessionContext implements N
> this.delegate = checkNotNull(delegate);
> this.observationQueueLength = observationQueueLength;
> this.commitRateLimiter = commitRateLimiter;
> + this.mountInfoProvider = mountInfoProvider;
> SessionStats sessionStats = delegate.getSessionStats();
> sessionStats.setAttributes(attributes);
>
> @@ -317,6 +321,11 @@ public class SessionContext implements N
> return fastQueryResultSize;
> }
>
> + @Nullable
> + public MountInfoProvider getMountInfoProvider() {
> + return mountInfoProvider;
> + }
> +
> //-----------------------------------------------------< NamePathMapper
> >---
>
> @Override
>
> Modified:
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
> URL:
> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java?rev=1806602&r1=1806601&r2=1806602&view=diff
> ==============================================================================
> ---
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
> (original)
> +++
> jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
> Tue Aug 29 14:48:56 2017
> @@ -66,6 +66,7 @@ import org.apache.jackrabbit.oak.jcr.del
> import org.apache.jackrabbit.oak.jcr.security.AccessManager;
> import org.apache.jackrabbit.oak.jcr.session.operation.SessionOperation;
> import org.apache.jackrabbit.oak.jcr.xml.ImportHandler;
> +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
> import
> org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
> import
> org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
> import org.apache.jackrabbit.oak.stats.CounterStats;
> @@ -669,7 +670,7 @@ public class SessionImpl implements Jack
> // add-node needs to be checked on the (path of) the
> // new node that has/will be added
> String path = PathUtils.concat(tree.getPath(),
> sessionContext.getOakName(arguments[0].toString()));
> - return accessMgr.hasPermissions(path,
> Session.ACTION_ADD_NODE);
> + return accessMgr.hasPermissions(path,
> Session.ACTION_ADD_NODE) && !isMountedReadOnly(path);
> }
> } else if ("setPrimaryType".equals(methodName) ||
> "addMixin".equals(methodName) || "removeMixin".equals(methodName)) {
> permission = Permissions.NODE_TYPE_MANAGEMENT;
> @@ -685,7 +686,7 @@ public class SessionImpl implements Jack
> } else if ("remove".equals(methodName)) {
> permission = Permissions.REMOVE_NODE;
> }
> - return accessMgr.hasPermissions(tree, null, permission);
> + return accessMgr.hasPermissions(tree, null, permission) &&
> !isMountedReadOnly(tree.getPath());
> } else {
> if ("setValue".equals(methodName)) {
> permission = Permissions.MODIFY_PROPERTY;
> @@ -694,9 +695,11 @@ public class SessionImpl implements Jack
> }
> NodeDelegate parentDelegate = dlg.getParent();
> if (parentDelegate != null) {
> - return
> accessMgr.hasPermissions(parentDelegate.getTree(), ((PropertyDelegate)
> dlg).getPropertyState(), permission);
> + return
> accessMgr.hasPermissions(parentDelegate.getTree(), ((PropertyDelegate)
> dlg).getPropertyState(), permission)
> + && !isMountedReadOnly(parentDelegate.getPath());
> } else {
> - return accessMgr.hasPermissions(dlg.getPath(),
> (permission == Permissions.MODIFY_PROPERTY) ? Session.ACTION_SET_PROPERTY :
> Session.ACTION_REMOVE);
> + return accessMgr.hasPermissions(dlg.getPath(),
> (permission == Permissions.MODIFY_PROPERTY) ? Session.ACTION_SET_PROPERTY :
> Session.ACTION_REMOVE)
> + && !isMountedReadOnly(dlg.getPath());
> }
> }
> }
> @@ -704,6 +707,11 @@ public class SessionImpl implements Jack
> return true;
> }
>
> + private boolean isMountedReadOnly(String path) {
> + MountInfoProvider mip = sessionContext.getMountInfoProvider();
> + return mip != null && mip.getMountByPath(path).isReadOnly();
> + }
> +
> @Override
> @Nonnull
> public AccessControlManager getAccessControlManager() throws
> RepositoryException {
>
> Added:
> jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionImplCapabilityWithMountInfoProviderTest.java
> URL:
> http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionImplCapabilityWithMountInfoProviderTest.java?rev=1806602&view=auto
> ==============================================================================
> ---
> jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionImplCapabilityWithMountInfoProviderTest.java
> (added)
> +++
> jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/SessionImplCapabilityWithMountInfoProviderTest.java
> Tue Aug 29 14:48:56 2017
> @@ -0,0 +1,132 @@
> +/*
> + * 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.jcr.session;
> +
> +import static org.junit.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +
> +import java.util.Collections;
> +
> +import javax.jcr.Repository;
> +import javax.jcr.Session;
> +import javax.jcr.SimpleCredentials;
> +
> +import org.apache.jackrabbit.JcrConstants;
> +import org.apache.jackrabbit.oak.composite.CompositeNodeStore;
> +import org.apache.jackrabbit.oak.jcr.Jcr;
> +import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
> +import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
> +import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
> +import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
> +import org.apache.jackrabbit.oak.spi.mount.Mounts;
> +import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
> +import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
> +import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
> +import org.junit.Before;
> +import org.junit.Test;
> +
> +public class SessionImplCapabilityWithMountInfoProviderTest {
> +
> + private Session adminSession;
> +
> + @Before
> + public void prepare() throws Exception {
> + MountInfoProvider mip = Mounts.newBuilder().readOnlyMount("ro",
> "/private").build();
> +
> + MemoryNodeStore roStore = new MemoryNodeStore();
> + {
> + NodeBuilder builder = roStore.getRoot().builder();
> + builder
> + .child("private").setProperty(JcrConstants.JCR_PRIMARYTYPE,
> JcrConstants.NT_UNSTRUCTURED)
> + .setProperty("prop", "value")
> + .child("foo").setProperty(JcrConstants.JCR_PRIMARYTYPE,
> JcrConstants.NT_UNSTRUCTURED);
> + roStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
> + }
> +
> + MemoryNodeStore globalStore = new MemoryNodeStore();
> + {
> + NodeBuilder builder = globalStore.getRoot().builder();
> + builder
> + .child("foo").setProperty(JcrConstants.JCR_PRIMARYTYPE,
> JcrConstants.NT_UNSTRUCTURED)
> + .setProperty("prop", "value")
> + .child("bar").setProperty(JcrConstants.JCR_PRIMARYTYPE,
> JcrConstants.NT_UNSTRUCTURED);
> + globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
> + }
> +
> + CompositeNodeStore store = new CompositeNodeStore.Builder(mip,
> globalStore)
> + .addMount("ro", roStore)
> + .build();
> +
> + Whiteboard whiteboard = new DefaultWhiteboard();
> + whiteboard.register(MountInfoProvider.class, mip,
> Collections.emptyMap());
> +
> + Jcr jcr = new Jcr(store).with(whiteboard);
> + jcr.createContentRepository();
> + Repository repository = jcr.createRepository();
> +
> + adminSession = repository.login(new SimpleCredentials("admin",
> "admin".toCharArray()));
> + }
> +
> + @Test
> + public void addNode() throws Exception {
> +
> + // unable to add nodes in the read-only mount
> + assertFalse("Must not be able to add a child not under the private
> mount root",
> + adminSession.hasCapability("addNode",
> adminSession.getNode("/private"), new String[] {"foo"}));
> + assertFalse("Must not be able to add a child not under the private
> mount",
> + adminSession.hasCapability("addNode",
> adminSession.getNode("/private/foo"), new String[] {"bar"}));
> + // able to add nodes outside the read-only mount
> + assertTrue("Must be able to add a child node under the root",
> + adminSession.hasCapability("addNode",
> adminSession.getNode("/"), new String[] {"not-private"}));
> + // unable to add node at the root of the read-only mount ( even
> though it already exists )
> + assertFalse("Must not be able to add a child node in place of the
> private mount",
> + adminSession.hasCapability("addNode",
> adminSession.getNode("/"), new String[] {"private"}));
> + }
> +
> + @Test
> + public void orderBefore() throws Exception {
> + // able to order the root of the mount since the operation is
> performed on the parent
> + assertTrue(adminSession.hasCapability("orderBefore",
> adminSession.getNode("/private"), null));
> + assertFalse(adminSession.hasCapability("orderBefore",
> adminSession.getNode("/private/foo"), null));
> + }
> +
> + @Test
> + public void simpleNodeOperations() throws Exception {
> + for ( String operation : new String[] { "setPrimaryType",
> "addMixin", "removeMixin" , "setProperty", "remove"} ) {
> + for ( String privateMountNode : new String[] { "/private",
> "/private/foo" } ) {
> + assertFalse("Unexpected return value for hasCapability(" +
> operation+ ") on node '" + privateMountNode +"' from the private mount",
> + adminSession.hasCapability(operation,
> adminSession.getNode(privateMountNode), null));
> + }
> + String globalMountNode = "/foo";
> + assertTrue("Unexpected return value for hasCapability(" +
> operation+ ") on node '" + globalMountNode +"' from the global mount",
> + adminSession.hasCapability(operation,
> adminSession.getNode(globalMountNode), null));
> + }
> + }
> +
> + @Test
> + public void itemOperations() throws Exception {
> + for ( String operation : new String[] { "setValue", "remove"} ) {
> + String privateMountProp = "/private/prop";
> + String globalMountProp = "/foo/prop";
> +
> + assertFalse("Unexpected return value for hasCapability(" +
> operation+ ") on item '" + privateMountProp +"' from the private mount",
> + adminSession.hasCapability(operation,
> adminSession.getItem(privateMountProp), null));
> + assertTrue("Unexpected return value for hasCapability(" +
> operation+ ") on item '" + globalMountProp +"' from the global mount",
> + adminSession.hasCapability(operation,
> adminSession.getItem(globalMountProp), null));
> + }
> + }
> +}
>
>