You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2020/12/16 13:25:15 UTC

[geode] branch develop updated: GEODE-8782: Add getPrincipal method to FunctionContext interface (#5840)

This is an automated email from the ASF dual-hosted git repository.

jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new a42f89a  GEODE-8782: Add getPrincipal method to FunctionContext interface (#5840)
a42f89a is described below

commit a42f89ab48aaca9aac96a34e80a9b3b2d46bcddd
Author: Jens Deppe <jd...@pivotal.io>
AuthorDate: Wed Dec 16 05:24:23 2020 -0800

    GEODE-8782: Add getPrincipal method to FunctionContext interface (#5840)
    
    - Add the ability to retrieve the Principal from the FunctionContext
      when a SecurityManager is enabled.
---
 .../src/main/resources/japicmp_exceptions.json     |   2 +
 .../FunctionExecutionWithPrincipalDUnitTest.java   | 145 +++++++++++++++++++++
 .../codeAnalysis/sanctionedDataSerializables.txt   |   4 +-
 .../geode/cache/execute/FunctionContext.java       |  10 ++
 .../geode/internal/cache/PartitionedRegion.java    |  12 +-
 .../internal/cache/PartitionedRegionDataStore.java |   4 +-
 .../cache/execute/FunctionContextImpl.java         |  17 +++
 .../cache/execute/FunctionRemoteContext.java       |  20 ++-
 .../cache/execute/RegionFunctionContextImpl.java   |  28 +++-
 .../PartitionedRegionFunctionStreamingMessage.java |   2 +-
 .../security/IntegratedSecurityService.java        |  14 ++
 .../internal/security/LegacySecurityService.java   |   5 +
 .../geode/internal/security/SecurityService.java   |   2 +
 .../geode/test/junit/rules/ClientCacheRule.java    |   7 +-
 .../internal/security/TestFunctions.java           |  26 +++-
 15 files changed, 279 insertions(+), 19 deletions(-)

diff --git a/buildSrc/src/main/resources/japicmp_exceptions.json b/buildSrc/src/main/resources/japicmp_exceptions.json
index 01317fc..b02a483 100755
--- a/buildSrc/src/main/resources/japicmp_exceptions.json
+++ b/buildSrc/src/main/resources/japicmp_exceptions.json
@@ -1,4 +1,6 @@
 {
   "Class org.apache.geode.net.SSLParameterExtension": "Old implementation exposed an internal class",
   "Method org.apache.geode.net.SSLParameterExtension.init(org.apache.geode.distributed.internal.DistributionConfig)": "Old implementation exposed an internal class",
+  "Class org.apache.geode.cache.execute.FunctionContext": "Interface not intended for client applications",
+  "Method org.apache.geode.cache.execute.FunctionContext.getPrincipal()": "Interface not intended for client applications"
 }
diff --git a/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/execute/FunctionExecutionWithPrincipalDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/execute/FunctionExecutionWithPrincipalDUnitTest.java
new file mode 100644
index 0000000..e9d5281
--- /dev/null
+++ b/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/execute/FunctionExecutionWithPrincipalDUnitTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.geode.internal.cache.execute;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.examples.SimpleSecurityManager;
+import org.apache.geode.management.internal.security.TestFunctions.ReadFunction;
+import org.apache.geode.test.dunit.rules.ClusterStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.dunit.rules.SerializableFunction;
+import org.apache.geode.test.junit.rules.ClientCacheRule;
+import org.apache.geode.test.junit.rules.ServerStarterRule;
+
+public class FunctionExecutionWithPrincipalDUnitTest {
+
+  private static String PR_REGION_NAME = "partitioned-region";
+  private static String REGION_NAME = "replicated-region";
+  private static Region<String, String> replicateRegion;
+  private static Region<String, String> partitionedRegion;
+
+  private static Function<?> readFunction = new ReadFunction();
+
+  private static MemberVM locator;
+  private static MemberVM server1;
+  private static MemberVM server2;
+  private static ClientCache client;
+
+  @ClassRule
+  public static ClusterStartupRule cluster = new ClusterStartupRule();
+
+  @ClassRule
+  public static ClientCacheRule clientRule = new ClientCacheRule();
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    locator = cluster.startLocatorVM(0, x -> x
+        .withSecurityManager(SimpleSecurityManager.class));
+    int locatorPort = locator.getPort();
+
+    SerializableFunction<ServerStarterRule> startupFunction = x -> x
+        .withConnectionToLocator(locatorPort)
+        .withCredential("cluster", "cluster")
+        .withProperty(ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER,
+            "org.apache.geode.management.internal.security.TestFunctions*");
+
+    server1 = cluster.startServerVM(1, startupFunction);
+    server2 = cluster.startServerVM(2, startupFunction);
+
+    Stream.of(server1, server2).forEach(v -> v.invoke(() -> {
+      ClusterStartupRule.getCache().createRegionFactory(RegionShortcut.REPLICATE).create(
+          REGION_NAME);
+      ClusterStartupRule.getCache().createRegionFactory(RegionShortcut.PARTITION_REDUNDANT)
+          .create(PR_REGION_NAME);
+    }));
+
+    client = clientRule
+        .withLocatorConnection(locatorPort)
+        .withCredential("data", "data")
+        .createCache();
+
+    replicateRegion = clientRule.createProxyRegion(REGION_NAME);
+    partitionedRegion = clientRule.createProxyRegion(PR_REGION_NAME);
+
+    for (int i = 0; i < 10; i++) {
+      replicateRegion.put("key-" + i, "value-" + i);
+      partitionedRegion.put("key-" + i, "value-" + i);
+    }
+  }
+
+  @Test
+  public void verifyPrincipal_whenUsingReplicateRegion_andCallingOnRegion() {
+    FunctionService.onRegion(replicateRegion)
+        .execute(readFunction)
+        .getResult();
+  }
+
+  @Test
+  public void verifyPrincipal_whenUsingPartitionedRegion_andCallingOnRegion() {
+    FunctionService.onRegion(partitionedRegion)
+        .execute(readFunction)
+        .getResult();
+  }
+
+  @Test
+  public void verifyPrincipal_whenUsingPartitionedRegion_andCallingOnRegion_withFilter() {
+    Set<String> filter = new HashSet<>();
+    filter.add("key-1");
+    filter.add("key-2");
+    filter.add("key-4");
+    filter.add("key-7");
+
+    FunctionService.onRegion(partitionedRegion)
+        .withFilter(filter)
+        .execute(readFunction)
+        .getResult();
+  }
+
+  @Test
+  public void verifyPrincipal_whenUsingPartitionedRegion_andCallingOnServer() {
+    FunctionService.onServer(partitionedRegion.getRegionService())
+        .execute(readFunction)
+        .getResult();
+  }
+
+  @Test
+  public void verifyPrincipal_whenUsingPartitionedRegion_andCallingOnServers() {
+    FunctionService.onServers(partitionedRegion.getRegionService())
+        .execute(readFunction)
+        .getResult();
+  }
+
+  @Test
+  public void verifyPrincipal_whenUsingReplicateRegion_andCallingOnServers() {
+    FunctionService.onServers(replicateRegion.getRegionService())
+        .execute(readFunction)
+        .getResult();
+  }
+
+}
diff --git a/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt b/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
index 204be50..65d8796 100644
--- a/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
+++ b/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
@@ -1297,8 +1297,8 @@ fromData,22
 toData,19
 
 org/apache/geode/internal/cache/execute/FunctionRemoteContext,2
-fromData,124
-toData,102
+fromData,145
+toData,123
 
 org/apache/geode/internal/cache/ha/HARegionQueue$DispatchedAndCurrentEvents,2
 fromData,37
diff --git a/geode-core/src/main/java/org/apache/geode/cache/execute/FunctionContext.java b/geode-core/src/main/java/org/apache/geode/cache/execute/FunctionContext.java
index bc91180..a6a779f 100755
--- a/geode-core/src/main/java/org/apache/geode/cache/execute/FunctionContext.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/execute/FunctionContext.java
@@ -14,10 +14,12 @@
  */
 package org.apache.geode.cache.execute;
 
+
 import org.apache.logging.log4j.util.Strings;
 
 import org.apache.geode.cache.Cache;
 import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.security.LegacySecurityService;
 
 /**
  * Defines the execution context of a {@link Function}. It is required by the
@@ -97,4 +99,12 @@ public interface FunctionContext<T1> {
 
     return member.getId();
   }
+
+  /**
+   * If available, returns the principal that has been authenticated to execute this function. This
+   * will always be null if the {@link LegacySecurityService} is in use.
+   *
+   * @return the principal that has been authenticated
+   */
+  Object getPrincipal();
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java b/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java
index afafb8b..4cb63d3 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java
@@ -3740,7 +3740,7 @@ public class PartitionedRegion extends LocalRegion
         FunctionRemoteContext context = new FunctionRemoteContext(function,
             execution.getArgumentsForMember(recip.getId()), memKeys,
             FunctionExecutionNodePruner.getBucketSet(this, memKeys, false, isBucketSetAsFilter),
-            execution.isReExecute(), execution.isFnSerializationReqd());
+            execution.isReExecute(), execution.isFnSerializationReqd(), getPrincipal());
         recipMap.put(recip, context);
       }
       if (logger.isDebugEnabled()) {
@@ -3755,6 +3755,10 @@ public class PartitionedRegion extends LocalRegion
     return localResultCollector;
   }
 
+  private Object getPrincipal() {
+    return cache.getSecurityService().getPrincipal();
+  }
+
   /**
    * Single key execution on single node
    *
@@ -3958,7 +3962,7 @@ public class PartitionedRegion extends LocalRegion
     for (InternalDistributedMember recip : dest) {
       FunctionRemoteContext context = new FunctionRemoteContext(function,
           execution.getArgumentsForMember(recip.getId()), null, memberToBuckets.get(recip),
-          execution.isReExecute(), execution.isFnSerializationReqd());
+          execution.isReExecute(), execution.isFnSerializationReqd(), getPrincipal());
       recipMap.put(recip, context);
     }
     final LocalResultCollector<?, ?> localRC = execution.getLocalResultCollector(function, rc);
@@ -4052,7 +4056,7 @@ public class PartitionedRegion extends LocalRegion
     for (InternalDistributedMember recip : memberToBuckets.keySet()) {
       FunctionRemoteContext context = new FunctionRemoteContext(function,
           execution.getArgumentsForMember(recip.getId()), null, memberToBuckets.get(recip),
-          execution.isReExecute(), execution.isFnSerializationReqd());
+          execution.isReExecute(), execution.isFnSerializationReqd(), getPrincipal());
       recipMap.put(recip, context);
     }
     final LocalResultCollector<?, ?> localResultCollector =
@@ -4984,7 +4988,7 @@ public class PartitionedRegion extends LocalRegion
             resultSender);
 
     FunctionRemoteContext context = new FunctionRemoteContext(function, object, routingKeys,
-        bucketArray, execution.isReExecute(), execution.isFnSerializationReqd());
+        bucketArray, execution.isReExecute(), execution.isFnSerializationReqd(), getPrincipal());
 
     HashMap<InternalDistributedMember, FunctionRemoteContext> recipMap =
         new HashMap<InternalDistributedMember, FunctionRemoteContext>();
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegionDataStore.java b/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegionDataStore.java
index 23a7487..d1d3dba 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegionDataStore.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegionDataStore.java
@@ -2969,7 +2969,7 @@ public class PartitionedRegionDataStore implements HasCachePerfStats {
   public void executeOnDataStore(final Set localKeys, final Function function, final Object object,
       final int prid, final int[] bucketArray, final boolean isReExecute,
       final PartitionedRegionFunctionStreamingMessage msg, long time, ServerConnection servConn,
-      int transactionID) {
+      int transactionID, Object principal) {
 
     if (!areAllBucketsHosted(bucketArray)) {
       throw new BucketMovedException(
@@ -2984,7 +2984,7 @@ public class PartitionedRegionDataStore implements HasCachePerfStats {
         new RegionFunctionContextImpl(getPartitionedRegion().getCache(), function.getId(),
             this.partitionedRegion, object, localKeys, ColocationHelper
                 .constructAndGetAllColocatedLocalDataSet(this.partitionedRegion, bucketArray),
-            bucketArray, resultSender, isReExecute);
+            bucketArray, resultSender, isReExecute, principal);
 
     FunctionStats stats = FunctionStatsManager.getFunctionStats(function.getId(), dm.getSystem());
     long start = stats.startFunctionExecution(function.hasResult());
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionContextImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionContextImpl.java
index c328b2f..c3ef8a7 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionContextImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionContextImpl.java
@@ -21,6 +21,7 @@ import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.cache.execute.RegionFunctionContext;
 import org.apache.geode.cache.execute.ResultSender;
+import org.apache.geode.internal.cache.InternalCache;
 
 /**
  * Context available to application functions which is passed from GemFire to {@link Function}. <br>
@@ -45,6 +46,8 @@ public class FunctionContextImpl implements FunctionContext {
 
   private final boolean isPossDup;
 
+  private final Object principal;
+
   public FunctionContextImpl(final Cache cache, final String functionId, final Object args,
       ResultSender resultSender) {
     this(cache, functionId, args, resultSender, false);
@@ -57,6 +60,14 @@ public class FunctionContextImpl implements FunctionContext {
     this.args = args;
     this.resultSender = resultSender;
     this.isPossDup = isPossibleDuplicate;
+
+    Object tmpPrincipal = null;
+    if (cache != null) {
+      if (((InternalCache) cache).getSecurityService() != null) {
+        tmpPrincipal = ((InternalCache) cache).getSecurityService().getPrincipal();
+      }
+    }
+    this.principal = tmpPrincipal;
   }
 
   /**
@@ -89,6 +100,8 @@ public class FunctionContextImpl implements FunctionContext {
     buf.append(this.functionId);
     buf.append(";args=");
     buf.append(this.args);
+    buf.append(";principal=");
+    buf.append(getPrincipal());
     buf.append(']');
     return buf.toString();
   }
@@ -111,4 +124,8 @@ public class FunctionContextImpl implements FunctionContext {
     return cache;
   }
 
+  @Override
+  public Object getPrincipal() {
+    return principal;
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionRemoteContext.java b/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionRemoteContext.java
index 9191a87..32d69d6 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionRemoteContext.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/execute/FunctionRemoteContext.java
@@ -31,8 +31,6 @@ import org.apache.geode.internal.serialization.StaticSerialization;
 
 /**
  * FunctionContext for remote/target nodes
- *
- *
  */
 public class FunctionRemoteContext implements DataSerializable {
 
@@ -50,16 +48,19 @@ public class FunctionRemoteContext implements DataSerializable {
 
   private Function function;
 
+  private Object principal;
+
   public FunctionRemoteContext() {}
 
   public FunctionRemoteContext(final Function function, Object object, Set filter,
-      int[] bucketArray, boolean isReExecute, boolean isFnSerializationReqd) {
+      int[] bucketArray, boolean isReExecute, boolean isFnSerializationReqd, Object principal) {
     this.function = function;
     this.args = object;
     this.filter = filter;
     this.bucketArray = bucketArray;
     this.isReExecute = isReExecute;
     this.isFnSerializationReqd = isFnSerializationReqd;
+    this.principal = principal;
   }
 
   @Override
@@ -84,6 +85,10 @@ public class FunctionRemoteContext implements DataSerializable {
       this.bucketArray = BucketSetHelper.fromSet(bucketSet);
     }
     this.isReExecute = DataSerializer.readBoolean(in);
+
+    if (StaticSerialization.getVersionForDataStream(in).isNotOlderThan(KnownVersion.GEODE_1_14_0)) {
+      this.principal = DataSerializer.readObject(in);
+    }
   }
 
   @Override
@@ -103,6 +108,11 @@ public class FunctionRemoteContext implements DataSerializable {
       DataSerializer.writeHashSet((HashSet) bucketSet, out);
     }
     DataSerializer.writeBoolean(this.isReExecute, out);
+
+    if (StaticSerialization.getVersionForDataStream(out)
+        .isNotOlderThan(KnownVersion.GEODE_1_14_0)) {
+      DataSerializer.writeObject(this.principal, out);
+    }
   }
 
   public Set getFilter() {
@@ -129,6 +139,10 @@ public class FunctionRemoteContext implements DataSerializable {
     return functionId;
   }
 
+  public Object getPrincipal() {
+    return principal;
+  }
+
   @Override
   public String toString() {
 
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/execute/RegionFunctionContextImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/execute/RegionFunctionContextImpl.java
index c8d9747..f4e25e0 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/execute/RegionFunctionContextImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/execute/RegionFunctionContextImpl.java
@@ -25,6 +25,7 @@ import org.apache.geode.cache.Region;
 import org.apache.geode.cache.execute.Execution;
 import org.apache.geode.cache.execute.FunctionService;
 import org.apache.geode.cache.execute.ResultSender;
+import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.internal.cache.LocalDataSet;
 
 /**
@@ -51,16 +52,32 @@ public class RegionFunctionContextImpl extends FunctionContextImpl
 
   private final boolean isPossibleDuplicate;
 
-  public RegionFunctionContextImpl(final Cache cache, final String functionId, final Region dataSet,
-      final Object args, final Set<?> routingObjects,
+  private final Object principal;
+
+  public RegionFunctionContextImpl(final Cache cache, final String functionId,
+      final Region<?, ?> dataSet, final Object args, final Set<?> routingObjects,
       final Map<String, LocalDataSet> colocatedLocalDataMap, int[] localBucketArray,
       ResultSender<?> resultSender, boolean isPossibleDuplicate) {
+    this(cache, functionId, dataSet, args, routingObjects, colocatedLocalDataMap, localBucketArray,
+        resultSender, isPossibleDuplicate, null);
+  }
+
+  public RegionFunctionContextImpl(final Cache cache, final String functionId,
+      final Region<?, ?> dataSet, final Object args, final Set<?> routingObjects,
+      final Map<String, LocalDataSet> colocatedLocalDataMap, int[] localBucketArray,
+      ResultSender<?> resultSender, boolean isPossibleDuplicate, Object principal) {
     super(cache, functionId, args, resultSender);
     this.dataSet = dataSet;
     this.filter = routingObjects;
     this.colocatedLocalDataMap = colocatedLocalDataMap;
     this.localBucketArray = localBucketArray;
     this.isPossibleDuplicate = isPossibleDuplicate;
+
+    if (principal == null) {
+      this.principal = ((InternalCache) cache).getSecurityService().getPrincipal();
+    } else {
+      this.principal = principal;
+    }
     setFunctionContexts();
   }
 
@@ -107,6 +124,8 @@ public class RegionFunctionContextImpl extends FunctionContextImpl
     buf.append(this.filter);
     buf.append(";args=");
     buf.append(getArguments());
+    buf.append(";principal=");
+    buf.append(getPrincipal());
     buf.append(']');
     return buf.toString();
   }
@@ -144,4 +163,9 @@ public class RegionFunctionContextImpl extends FunctionContextImpl
     }
     return this.localBucketArray;
   }
+
+  @Override
+  public Object getPrincipal() {
+    return principal;
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/partitioned/PartitionedRegionFunctionStreamingMessage.java b/geode-core/src/main/java/org/apache/geode/internal/cache/partitioned/PartitionedRegionFunctionStreamingMessage.java
index 23e9d4b2..52b6f98 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/partitioned/PartitionedRegionFunctionStreamingMessage.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/partitioned/PartitionedRegionFunctionStreamingMessage.java
@@ -100,7 +100,7 @@ public class PartitionedRegionFunctionStreamingMessage extends PartitionMessage
       // if null call executeOnDataStore otherwise execute on LocalBuckets
       ds.executeOnDataStore(context.getFilter(), context.getFunction(), context.getArgs(),
           getProcessorId(), context.getBucketArray(), context.isReExecute(), this, startTime, null,
-          0);
+          0, context.getPrincipal());
 
       if (!this.replyLastMsg && context.getFunction().hasResult()) {
         sendReply(getSender(), getProcessorId(), dm,
diff --git a/geode-core/src/main/java/org/apache/geode/internal/security/IntegratedSecurityService.java b/geode-core/src/main/java/org/apache/geode/internal/security/IntegratedSecurityService.java
index 2d1a703..eaf71ca 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/security/IntegratedSecurityService.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/security/IntegratedSecurityService.java
@@ -127,6 +127,20 @@ public class IntegratedSecurityService implements SecurityService {
   }
 
   /**
+   * Returns the current principal if one exists or null.
+   *
+   * @return the principal
+   */
+  @Override
+  public Object getPrincipal() {
+    try {
+      return getSubject().getPrincipal();
+    } catch (Exception ex) {
+      return null;
+    }
+  }
+
+  /**
    * @return return a shiro subject
    */
   @Override
diff --git a/geode-core/src/main/java/org/apache/geode/internal/security/LegacySecurityService.java b/geode-core/src/main/java/org/apache/geode/internal/security/LegacySecurityService.java
index d097fd9..885c515 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/security/LegacySecurityService.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/security/LegacySecurityService.java
@@ -69,6 +69,11 @@ public class LegacySecurityService implements SecurityService {
   }
 
   @Override
+  public Object getPrincipal() {
+    return null;
+  }
+
+  @Override
   public Subject login(Properties credentials) {
     return null;
   }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/security/SecurityService.java b/geode-core/src/main/java/org/apache/geode/internal/security/SecurityService.java
index 3eccdd9..89a415e 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/security/SecurityService.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/security/SecurityService.java
@@ -33,6 +33,8 @@ public interface SecurityService {
 
   Subject getSubject();
 
+  Object getPrincipal();
+
   Subject login(Properties credentials);
 
   void logout();
diff --git a/geode-dunit/src/main/java/org/apache/geode/test/junit/rules/ClientCacheRule.java b/geode-dunit/src/main/java/org/apache/geode/test/junit/rules/ClientCacheRule.java
index dbcfd8c..5befc5e 100644
--- a/geode-dunit/src/main/java/org/apache/geode/test/junit/rules/ClientCacheRule.java
+++ b/geode-dunit/src/main/java/org/apache/geode/test/junit/rules/ClientCacheRule.java
@@ -28,6 +28,7 @@ import org.apache.geode.cache.Region;
 import org.apache.geode.cache.RegionService;
 import org.apache.geode.cache.client.ClientCache;
 import org.apache.geode.cache.client.ClientCacheFactory;
+import org.apache.geode.cache.client.ClientRegionFactory;
 import org.apache.geode.cache.client.ClientRegionShortcut;
 import org.apache.geode.internal.net.SocketCreatorFactory;
 import org.apache.geode.security.templates.UserPasswordAuthInit;
@@ -109,8 +110,10 @@ public class ClientCacheRule extends SerializableExternalResource {
     return cache;
   }
 
-  public Region createProxyRegion(String regionPath) {
-    return cache.createClientRegionFactory(ClientRegionShortcut.PROXY).create(regionPath);
+  public <K, V> Region<K, V> createProxyRegion(String regionPath) {
+    ClientRegionFactory<K, V> regionFactory =
+        cache.createClientRegionFactory(ClientRegionShortcut.PROXY);
+    return regionFactory.create(regionPath);
   }
 
   public RegionService createAuthenticatedView(String username, String password) {
diff --git a/geode-junit/src/main/java/org/apache/geode/management/internal/security/TestFunctions.java b/geode-junit/src/main/java/org/apache/geode/management/internal/security/TestFunctions.java
index 8167937..423058c 100644
--- a/geode-junit/src/main/java/org/apache/geode/management/internal/security/TestFunctions.java
+++ b/geode-junit/src/main/java/org/apache/geode/management/internal/security/TestFunctions.java
@@ -17,6 +17,7 @@ package org.apache.geode.management.internal.security;
 
 import static org.apache.geode.security.ResourcePermission.Operation.READ;
 import static org.apache.geode.security.ResourcePermission.Resource.DATA;
+import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.Serializable;
 import java.util.Collection;
@@ -27,11 +28,12 @@ import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.security.ResourcePermission;
 
 public final class TestFunctions implements Serializable {
-  public static class WriteFunction implements Function {
+  public static class WriteFunction implements Function<Object> {
     public static final String SUCCESS_OUTPUT = "writeDataFunctionSucceeded";
 
     @Override
-    public void execute(FunctionContext context) {
+    public void execute(FunctionContext<Object> context) {
+      verifyPrincipal(context);
       context.getResultSender().lastResult(SUCCESS_OUTPUT);
     }
 
@@ -39,13 +41,19 @@ public final class TestFunctions implements Serializable {
     public String getId() {
       return "writeData";
     }
+
+    @Override
+    public boolean optimizeForWrite() {
+      return true;
+    }
   }
 
   public static class ReadFunction implements Function<Object> {
     public static final String SUCCESS_OUTPUT = "readDataFunctionSucceeded";
 
     @Override
-    public void execute(FunctionContext context) {
+    public void execute(FunctionContext<Object> context) {
+      verifyPrincipal(context);
       context.getResultSender().lastResult(SUCCESS_OUTPUT);
     }
 
@@ -58,5 +66,17 @@ public final class TestFunctions implements Serializable {
     public String getId() {
       return "readData";
     }
+
+    @Override
+    public boolean optimizeForWrite() {
+      return true;
+    }
+  }
+
+  private static void verifyPrincipal(FunctionContext<Object> context) {
+    String principal = (String) context.getPrincipal();
+    assertThat(principal).as("Principal cannot be null").isNotNull();
+    assertThat(principal).startsWith("data");
+
   }
 }