This is an automated email from the ASF dual-hosted git repository. jinmeiliao pushed a commit to branch expireAuthentication in repository https://gitbox.apache.org/repos/asf/geode.git
commit 5bbd1c9e33b130f0ae0eec77bf7316681a457714 Author: Jinmei Liao <[email protected]> AuthorDate: Mon Aug 23 08:58:40 2021 -0700 GEODE-9521: Add test to cover multi-servers scenario for re-authentic… (#6782) --- .../geode/security/AuthExpirationDUnitTest.java | 65 +++++----- .../AuthExpirationMultiServerDUnitTest.java | 136 +++++++++++++++++++++ .../geode/security/ExpirableSecurityManager.java | 29 +++-- 3 files changed, 194 insertions(+), 36 deletions(-) diff --git a/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationDUnitTest.java b/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationDUnitTest.java index 0aae286..8c8eaff 100644 --- a/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationDUnitTest.java +++ b/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationDUnitTest.java @@ -48,8 +48,8 @@ import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactor @RunWith(Parameterized.class) @Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class) public class AuthExpirationDUnitTest { - static RegionService regionService0; - static RegionService regionService1; + static RegionService user0Service; + static RegionService user1Service; @Parameterized.Parameter public String clientVersion; @@ -110,9 +110,14 @@ public class AuthExpirationDUnitTest { // all put operation succeeded Region<Object, Object> region = server.getCache().getRegion("/region"); - assertThat(ExpirableSecurityManager.getExpiredUsers().size()).isEqualTo(1); - assertThat(ExpirableSecurityManager.getExpiredUsers().contains("user1")).isTrue(); assertThat(region.size()).isEqualTo(2); + Map<String, List<String>> authorizedOps = ExpirableSecurityManager.getAuthorizedOps(); + Map<String, List<String>> unAuthorizedOps = ExpirableSecurityManager.getUnAuthorizedOps(); + assertThat(authorizedOps.keySet().size()).isEqualTo(2); + assertThat(authorizedOps.get("user1")).asList().containsExactly("DATA:WRITE:region:0"); + assertThat(authorizedOps.get("user2")).asList().containsExactly("DATA:WRITE:region:1"); + assertThat(unAuthorizedOps.keySet().size()).isEqualTo(1); + assertThat(unAuthorizedOps.get("user1")).asList().containsExactly("DATA:WRITE:region:1"); } @Test @@ -125,48 +130,52 @@ public class AuthExpirationDUnitTest { .withServerConnection(serverPort)); clientVM.invoke(() -> { - UpdatableUserAuthInitialize.setUser("serviceUser0"); + UpdatableUserAuthInitialize.setUser("user0"); ClientCache clientCache = ClusterStartupRule.getClientCache(); - assert clientCache != null; clientCache.createClientRegionFactory(ClientRegionShortcut.PROXY).create("region"); Properties userSecurityProperties = new Properties(); userSecurityProperties.put(SECURITY_CLIENT_AUTH_INIT, UpdatableUserAuthInitialize.class.getName()); - regionService0 = clientCache.createAuthenticatedView(userSecurityProperties); - Region<Object, Object> region = regionService0.getRegion("/region"); + user0Service = clientCache.createAuthenticatedView(userSecurityProperties); + Region<Object, Object> region = user0Service.getRegion("/region"); region.put(0, "value0"); - UpdatableUserAuthInitialize.setUser("serviceUser1"); + UpdatableUserAuthInitialize.setUser("user1"); userSecurityProperties.put(SECURITY_CLIENT_AUTH_INIT, UpdatableUserAuthInitialize.class.getName()); - regionService1 = clientCache.createAuthenticatedView(userSecurityProperties); - region = regionService1.getRegion("/region"); + user1Service = clientCache.createAuthenticatedView(userSecurityProperties); + region = user1Service.getRegion("/region"); region.put(1, "value1"); }); - ExpirableSecurityManager.addExpiredUser("serviceUser1"); + ExpirableSecurityManager.addExpiredUser("user1"); clientVM.invoke(() -> { - Region<Object, Object> region = regionService1.getRegion("/region"); - UpdatableUserAuthInitialize.setUser("serviceUser2"); - region.put(2, "value2"); - - region = regionService0.getRegion("/region"); - region.put(3, "value3"); - regionService0.close(); - regionService1.close(); + + Region<Object, Object> region = user0Service.getRegion("/region"); + region.put(2, "value3"); + + UpdatableUserAuthInitialize.setUser("user1_extended"); + region = user1Service.getRegion("/region"); + region.put(3, "value2"); + + user0Service.close(); + user1Service.close(); }); Region<Object, Object> region = server.getCache().getRegion("/region"); - assertThat(ExpirableSecurityManager.getExpiredUsers().size()).isEqualTo(1); - assertThat(ExpirableSecurityManager.getExpiredUsers().contains("serviceUser1")).isTrue(); - Map<Object, List<ResourcePermission>> authorizedOps = - ExpirableSecurityManager.getAuthorizedOps(); - assertThat(authorizedOps.size()).isEqualTo(3); - assertThat(authorizedOps.get("serviceUser0").size()).isEqualTo(2); - assertThat(authorizedOps.get("serviceUser1").size()).isEqualTo(1); - assertThat(authorizedOps.get("serviceUser2").size()).isEqualTo(1); assertThat(region.size()).isEqualTo(4); + + Map<String, List<String>> authorizedOps = ExpirableSecurityManager.getAuthorizedOps(); + assertThat(authorizedOps.keySet().size()).isEqualTo(3); + assertThat(authorizedOps.get("user0")).asList().containsExactly("DATA:WRITE:region:0", + "DATA:WRITE:region:2"); + assertThat(authorizedOps.get("user1")).asList().containsExactly("DATA:WRITE:region:1"); + assertThat(authorizedOps.get("user1_extended")).asList().containsExactly("DATA:WRITE:region:3"); + + Map<String, List<String>> unAuthorizedOps = ExpirableSecurityManager.getUnAuthorizedOps(); + assertThat(unAuthorizedOps.keySet().size()).isEqualTo(1); + assertThat(unAuthorizedOps.get("user1")).asList().containsExactly("DATA:WRITE:region:3"); } } diff --git a/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationMultiServerDUnitTest.java b/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationMultiServerDUnitTest.java new file mode 100644 index 0000000..0dbc8ac --- /dev/null +++ b/geode-core/src/upgradeTest/java/org/apache/geode/security/AuthExpirationMultiServerDUnitTest.java @@ -0,0 +1,136 @@ +/* + * 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.security; + +import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_AUTH_INIT; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.test.dunit.rules.ClusterStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; +import org.apache.geode.test.junit.categories.SecurityTest; +import org.apache.geode.test.junit.rules.ClientCacheRule; + +@Category({SecurityTest.class}) +public class AuthExpirationMultiServerDUnitTest implements Serializable { + public static final String REPLICATE_REGION = "replicateRegion"; + public static final String PARTITION_REGION = "partitionRegion"; + private MemberVM locator, server1, server2; + private int locatorPort; + + @Rule + public ClusterStartupRule lsRule = new ClusterStartupRule(); + + @Rule + public ClientCacheRule clientCacheRule = new ClientCacheRule(); + + @Before + public void setup() { + locator = lsRule.startLocatorVM(0, l -> l.withSecurityManager(ExpirableSecurityManager.class)); + locatorPort = locator.getPort(); + server1 = lsRule.startServerVM(1, s -> s.withSecurityManager(ExpirableSecurityManager.class) + .withCredential("test", "test") + .withRegion(RegionShortcut.REPLICATE, REPLICATE_REGION) + .withRegion(RegionShortcut.PARTITION, PARTITION_REGION) + .withConnectionToLocator(locatorPort)); + server2 = lsRule.startServerVM(2, s -> s.withSecurityManager(ExpirableSecurityManager.class) + .withCredential("test", "test") + .withRegion(RegionShortcut.REPLICATE, REPLICATE_REGION) + .withRegion(RegionShortcut.PARTITION, PARTITION_REGION) + .withConnectionToLocator(locatorPort)); + } + + @Test + public void clientReAuthenticationWorksOnMultipleServers() throws Exception { + UpdatableUserAuthInitialize.setUser("user1"); + clientCacheRule + .withProperty(SECURITY_CLIENT_AUTH_INIT, UpdatableUserAuthInitialize.class.getName()) + .withPoolSubscription(true) + .withServerConnection(server1.getPort()); + clientCacheRule.createCache(); + Region<Object, Object> region1 = clientCacheRule.createProxyRegion(REPLICATE_REGION); + Region<Object, Object> region2 = clientCacheRule.createProxyRegion(PARTITION_REGION); + region1.put("0", "value0"); + region2.put("0", "value0"); + + expireUserOnAllVms("user1"); + + UpdatableUserAuthInitialize.setUser("user2"); + region1.put("1", "value1"); + region2.put("1", "value1"); + + // locator only validates peer + locator.invoke(() -> { + Map<String, List<String>> authorizedOps = ExpirableSecurityManager.getAuthorizedOps(); + assertThat(authorizedOps.keySet().contains("test")).isTrue(); + assertThat(authorizedOps.keySet().size()).isEqualTo(1); + Map<String, List<String>> unAuthorizedOps = ExpirableSecurityManager.getUnAuthorizedOps(); + assertThat(unAuthorizedOps.keySet().size()).isEqualTo(0); + }); + + // client is connected to server1, server1 gets all the initial contact, + // authorization checks happens here + server1.invoke(() -> { + Map<String, List<String>> authorizedOps = ExpirableSecurityManager.getAuthorizedOps(); + assertThat(authorizedOps.get("user1")).asList().containsExactlyInAnyOrder( + "DATA:WRITE:replicateRegion:0", "DATA:WRITE:partitionRegion:0"); + assertThat(authorizedOps.get("user2")).asList().containsExactlyInAnyOrder( + "DATA:WRITE:replicateRegion:1", "DATA:WRITE:partitionRegion:1"); + Map<String, List<String>> unAuthorizedOps = ExpirableSecurityManager.getUnAuthorizedOps(); + assertThat(unAuthorizedOps.get("user1")).asList() + .containsExactly("DATA:WRITE:replicateRegion:1"); + }); + + // server2 performs no authorization checks + server2.invoke(() -> { + Map<String, List<String>> authorizedOps = ExpirableSecurityManager.getAuthorizedOps(); + Map<String, List<String>> unAuthorizedOps = ExpirableSecurityManager.getUnAuthorizedOps(); + assertThat(authorizedOps.size()).isEqualTo(0); + assertThat(unAuthorizedOps.size()).isEqualTo(0); + }); + + MemberVM.invokeInEveryMember(() -> { + InternalCache cache = ClusterStartupRule.getCache(); + Region<Object, Object> serverRegion1 = cache.getRegion(REPLICATE_REGION); + assertThat(serverRegion1.size()).isEqualTo(2); + Region<Object, Object> serverRegion2 = cache.getRegion(PARTITION_REGION); + assertThat(serverRegion2.size()).isEqualTo(2); + }, server1, server2); + + MemberVM.invokeInEveryMember(() -> { + ExpirableSecurityManager.reset(); + UpdatableUserAuthInitialize.reset(); + }, locator, server1, server2); + } + + private void expireUserOnAllVms(String user) { + MemberVM.invokeInEveryMember(() -> { + ExpirableSecurityManager.addExpiredUser(user); + }, locator, server1, server2); + } + + +} diff --git a/geode-junit/src/main/java/org/apache/geode/security/ExpirableSecurityManager.java b/geode-junit/src/main/java/org/apache/geode/security/ExpirableSecurityManager.java index 5c4d177..59a8b4cb 100644 --- a/geode-junit/src/main/java/org/apache/geode/security/ExpirableSecurityManager.java +++ b/geode-junit/src/main/java/org/apache/geode/security/ExpirableSecurityManager.java @@ -35,20 +35,18 @@ public class ExpirableSecurityManager extends SimpleSecurityManager { // use static field for ease of testing since there is only one instance of this in each VM // we only need ConcurrentHashSet here, but map is only construct available in the library private static final Set<String> EXPIRED_USERS = ConcurrentHashMap.newKeySet(); - private static final Map<Object, List<ResourcePermission>> AUTHORIZED_OPS = + private static final Map<String, List<String>> AUTHORIZED_OPS = + new ConcurrentHashMap<>(); + private static final Map<String, List<String>> UNAUTHORIZED_OPS = new ConcurrentHashMap<>(); @Override public boolean authorize(Object principal, ResourcePermission permission) { if (EXPIRED_USERS.contains((String) principal)) { + addToMap(UNAUTHORIZED_OPS, principal, permission); throw new AuthenticationExpiredException("User authentication expired."); } - List<ResourcePermission> permissions = AUTHORIZED_OPS.get(principal); - if (permissions == null) { - permissions = new ArrayList<>(); - } - permissions.add(permission); - AUTHORIZED_OPS.put(principal, permissions); + addToMap(AUTHORIZED_OPS, principal, permission); // always authorized return true; @@ -62,12 +60,27 @@ public class ExpirableSecurityManager extends SimpleSecurityManager { return EXPIRED_USERS; } - public static Map<Object, List<ResourcePermission>> getAuthorizedOps() { + public static Map<String, List<String>> getAuthorizedOps() { return AUTHORIZED_OPS; } + public static Map<String, List<String>> getUnAuthorizedOps() { + return UNAUTHORIZED_OPS; + } + + private static void addToMap(Map<String, List<String>> maps, Object user, + ResourcePermission permission) { + List<String> list = maps.get(user); + if (list == null) { + list = new ArrayList<>(); + } + list.add(permission.toString()); + maps.put(user.toString(), list); + } + public static void reset() { EXPIRED_USERS.clear(); AUTHORIZED_OPS.clear(); + UNAUTHORIZED_OPS.clear(); } }
