You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ji...@apache.org on 2019/06/27 18:38:15 UTC
[geode] branch develop updated: GEODE-6897: re-organize all the
security tests and add missing permissions to controllers. (#3744)
This is an automated email from the ASF dual-hosted git repository.
jinmeiliao 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 c243ac0 GEODE-6897: re-organize all the security tests and add missing permissions to controllers. (#3744)
c243ac0 is described below
commit c243ac03a89aa5abaaed52bae182be226e5d6652
Author: Jinmei Liao <ji...@pivotal.io>
AuthorDate: Thu Jun 27 11:38:02 2019 -0700
GEODE-6897: re-organize all the security tests and add missing permissions to controllers. (#3744)
---
.../RegionManagementRestSecurityDUnitTest.java | 127 -------------
.../RegionManagementSecurityRestDUnitTest.java | 206 ---------------------
...usterManagementSecurityRestIntegrationTest.java | 176 ++++++++++++++++++
.../controllers/RegionManagementController.java | 2 +
4 files changed, 178 insertions(+), 333 deletions(-)
diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementRestSecurityDUnitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementRestSecurityDUnitTest.java
deleted file mode 100644
index 2489fcc..0000000
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementRestSecurityDUnitTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.management.internal.rest;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.util.Properties;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import org.apache.geode.cache.configuration.RegionConfig;
-import org.apache.geode.cache.configuration.RegionType;
-import org.apache.geode.examples.SimpleSecurityManager;
-import org.apache.geode.management.api.ClusterManagementResult;
-import org.apache.geode.management.api.RealizationResult;
-import org.apache.geode.test.dunit.rules.ClusterStartupRule;
-import org.apache.geode.test.dunit.rules.MemberVM;
-import org.apache.geode.test.junit.rules.GeodeDevRestClient;
-
-public class RegionManagementRestSecurityDUnitTest {
- @ClassRule
- public static ClusterStartupRule cluster = new ClusterStartupRule();
-
- private static MemberVM locator, server;
-
- private static GeodeDevRestClient restClient;
-
- private static Properties config;
-
- private static String json;
-
- @BeforeClass
- public static void beforeClass() throws Exception {
- locator = cluster.startLocatorVM(0, l -> l.withHttpService()
- .withSecurityManager(SimpleSecurityManager.class));
-
- config = new Properties();
- config.setProperty("security-username", "cluster");
- config.setProperty("security-password", "cluster");
-
- server = cluster.startServerVM(1, config, locator.getPort());
- restClient =
- new GeodeDevRestClient("/management/v2", "localhost", locator.getHttpPort(), false);
-
- RegionConfig regionConfig = new RegionConfig();
- regionConfig.setName("customers");
- regionConfig.setType(RegionType.REPLICATE);
- ObjectMapper mapper = new ObjectMapper();
- json = mapper.writeValueAsString(regionConfig);
- }
-
- @Test
- public void createRegionWithoutCredentials_failsWithAuthenticationError() throws Exception {
- ClusterManagementResult<?> result =
- restClient.doPostAndAssert("/regions", json)
- .hasStatusCode(401)
- .getClusterManagementResult();
-
- assertThat(result.isSuccessful()).isFalse();
- assertThat(result.getStatusCode())
- .isEqualTo(ClusterManagementResult.StatusCode.UNAUTHENTICATED);
- assertThat(result.getStatusMessage()).contains("authentication is required");
- }
-
- @Test
- public void createRegionWithBadCredentials_failsWithAuthenticationError() throws Exception {
- ClusterManagementResult<?> result =
- restClient.doPostAndAssert("/regions", json, "baduser", "badpassword")
- .hasStatusCode(401)
- .getClusterManagementResult();
-
- assertThat(result.isSuccessful()).isFalse();
- assertThat(result.getStatusCode())
- .isEqualTo(ClusterManagementResult.StatusCode.UNAUTHENTICATED);
- assertThat(result.getStatusMessage()).contains("Authentication error");
- }
-
- @Test
- public void createRegionNotAuthorized_failsWithAuthorizationError() throws Exception {
- ClusterManagementResult<?> result =
- restClient.doPostAndAssert("/regions", json, "notauthorized", "notauthorized")
- .hasStatusCode(403)
- .getClusterManagementResult();
-
- assertThat(result.isSuccessful()).isFalse();
- assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.UNAUTHORIZED);
- assertThat(result.getStatusMessage()).contains("not authorized for DATA:MANAGE");
- }
-
- @Test
- public void createRegionWithCredentials_CreatesRegion() throws Exception {
- ClusterManagementResult<?> result =
- restClient.doPostAndAssert("/regions", json, "datamanage", "datamanage")
- .hasStatusCode(201)
- .getClusterManagementResult();
-
- assertThat(result.isSuccessful()).isTrue();
- assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.OK);
- assertThat(result.getMemberStatuses()).extracting(RealizationResult::getMemberName)
- .containsExactly("server-1");
-
- // make sure region is created
- server.invoke(() -> RegionManagementDunitTest.verifyRegionCreated("customers", "REPLICATE"));
-
- // make sure region is persisted
- locator.invoke(() -> RegionManagementDunitTest.verifyRegionPersisted("customers", "REPLICATE",
- "cluster"));
-
- // verify that additional server can be started with the cluster configuration
- cluster.startServerVM(2, config, locator.getPort());
- }
-}
diff --git a/geode-web-management/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementSecurityRestDUnitTest.java b/geode-web-management/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementSecurityRestDUnitTest.java
deleted file mode 100644
index b4e00cd..0000000
--- a/geode-web-management/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementSecurityRestDUnitTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.management.internal.rest;
-
-import static org.hamcrest.Matchers.is;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-import java.util.List;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
-import org.springframework.web.context.WebApplicationContext;
-
-import org.apache.geode.cache.configuration.RegionConfig;
-import org.apache.geode.cache.configuration.RegionType;
-import org.apache.geode.examples.SimpleSecurityManager;
-import org.apache.geode.management.api.ClusterManagementResult;
-import org.apache.geode.management.configuration.RuntimeRegionConfig;
-import org.apache.geode.management.internal.api.LocatorClusterManagementService;
-import org.apache.geode.test.dunit.rules.ClusterStartupRule;
-
-@RunWith(SpringRunner.class)
-@ContextConfiguration(locations = {"classpath*:WEB-INF/management-servlet.xml"},
- loader = SecuredLocatorContextLoader.class)
-@WebAppConfiguration
-public class RegionManagementSecurityRestDUnitTest {
-
- private static final String REGION = "products";
-
- @Autowired
- private WebApplicationContext webApplicationContext;
-
- @Rule
- public ClusterStartupRule cluster = new ClusterStartupRule(1);
-
- private LocatorWebContext context;
-
- private RegionConfig regionConfig;
- private String json;
-
- @Before
- public void before() throws JsonProcessingException {
- regionConfig = new RegionConfig();
- regionConfig.setName(REGION);
- regionConfig.setType(RegionType.REPLICATE);
- ObjectMapper mapper = new ObjectMapper();
- json = mapper.writeValueAsString(regionConfig);
- context = new LocatorWebContext(webApplicationContext);
-
- cluster.setSkipLocalDistributedSystemCleanup(true);
- int locatorPort = context.getLocator().getPort();
-
- cluster.startServerVM(1, s -> s.withConnectionToLocator(locatorPort)
- .withSecurityManager(SimpleSecurityManager.class)
- .withCredential("cluster", "cluster"));
- }
-
- @After
- public void after() {
- LocatorClusterManagementService client =
- (LocatorClusterManagementService) context.getLocator().getClusterManagementService();
-
- ClusterManagementResult<RuntimeRegionConfig> result = client.list(new RegionConfig());
- List<RuntimeRegionConfig> regions = result.getResult();
-
- regions.forEach(r -> client.delete(new RegionConfig(r)));
- }
-
- @Test
- public void createRegionFails_when_notAuthorized() throws Exception {
- context.perform(post("/v2/regions")
- .with(httpBasic("user", "user"))
- .content(json))
- .andExpect(status().isForbidden())
- .andExpect(jsonPath("$.statusCode", is("UNAUTHORIZED")))
- .andExpect(jsonPath("$.statusMessage",
- is("user not authorized for DATA:MANAGE")));
- }
-
- @Test
- public void createRegionFails_when_noCredentials() throws Exception {
- context.perform(post("/v2/regions")
- .content(json))
- .andExpect(status().isUnauthorized())
- .andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
- .andExpect(jsonPath("$.statusMessage",
- is("Full authentication is required to access this resource")));
- }
-
- @Test
- public void createRegionFails_when_wrongCredentials() throws Exception {
- context.perform(post("/v2/regions")
- .with(httpBasic("user", "wrong_password"))
- .content(json))
- .andExpect(status().isUnauthorized())
- .andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
- .andExpect(jsonPath("$.statusMessage",
- is("Authentication error. Please check your credentials.")));
- }
-
- @Test
- public void createRegion_succeeds() throws Exception {
- createRegion();
- }
-
- @Test
- public void deleteRegionFails_when_notAuthorized() throws Exception {
- createRegion();
-
- context.perform(delete("/v2/regions/" + REGION)
- .with(httpBasic("user", "user"))
- .content(json))
- .andExpect(status().isForbidden())
- .andExpect(jsonPath("$.statusCode", is("UNAUTHORIZED")))
- .andExpect(jsonPath("$.statusMessage",
- is("user not authorized for DATA:MANAGE")));
- }
-
- @Test
- public void deleteRegionFails_when_noCredentials() throws Exception {
- createRegion();
-
- context.perform(delete("/v2/regions/" + REGION)
- .content(json))
- .andExpect(status().isUnauthorized())
- .andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
- .andExpect(jsonPath("$.statusMessage",
- is("Full authentication is required to access this resource")));
- }
-
- @Test
- public void deleteRegionFails_when_wrongCredentials() throws Exception {
- createRegion();
-
- context.perform(delete("/v2/regions/" + REGION)
- .with(httpBasic("user", "wrong_password"))
- .content(json))
- .andExpect(status().isUnauthorized())
- .andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
- .andExpect(jsonPath("$.statusMessage",
- is("Authentication error. Please check your credentials.")));
- }
-
- @Test
- public void deleteRegion_succeeds() throws Exception {
- createRegion();
-
- context.perform(delete("/v2/regions/" + REGION)
- .with(httpBasic("dataManage", "dataManage"))
- .content(json))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.statusCode", is("OK")))
- .andExpect(jsonPath("$.statusMessage",
- is("Successfully removed config for [cluster]")));
- }
-
- @Test
- public void listRegion_succeeds() throws Exception {
- createRegion();
-
- context.perform(get("/v2/regions/" + REGION)
- .with(httpBasic("clusterRead", "clusterRead"))
- .content(json))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.statusCode", is("OK")))
- .andExpect(jsonPath("$.result[0].entryCount", is(0)));
- }
-
- private void createRegion() throws Exception {
- context.perform(post("/v2/regions")
- .with(httpBasic("dataManage", "dataManage"))
- .content(json))
- .andExpect(status().isCreated())
- .andExpect(jsonPath("$.statusCode", is("OK")))
- .andExpect(jsonPath("$.statusMessage",
- is("Successfully updated config for cluster")));
- }
-
-}
diff --git a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/ClusterManagementSecurityRestIntegrationTest.java b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/ClusterManagementSecurityRestIntegrationTest.java
new file mode 100644
index 0000000..6807699
--- /dev/null
+++ b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/ClusterManagementSecurityRestIntegrationTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.management.internal.rest;
+
+import static org.hamcrest.Matchers.is;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
+import org.springframework.web.context.WebApplicationContext;
+
+import org.apache.geode.cache.configuration.GatewayReceiverConfig;
+import org.apache.geode.cache.configuration.PdxType;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.cache.configuration.RegionType;
+import org.apache.geode.util.internal.GeodeJsonMapper;
+
+@RunWith(SpringRunner.class)
+@ContextConfiguration(locations = {"classpath*:WEB-INF/management-servlet.xml"},
+ loader = SecuredLocatorContextLoader.class)
+@WebAppConfiguration
+public class ClusterManagementSecurityRestIntegrationTest {
+
+ private static final String REGION = "products";
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ private LocatorWebContext context;
+
+ private static List<TestContext> testContexts = new ArrayList<>();
+ private static ObjectMapper mapper;
+
+ @BeforeClass
+ public static void beforeClass() throws JsonProcessingException {
+ mapper = GeodeJsonMapper.getMapper();
+ RegionConfig regionConfig = new RegionConfig();
+ regionConfig.setName(REGION);
+ regionConfig.setType(RegionType.REPLICATE);
+
+ testContexts.add(new TestContext(post("/v2/regions"), "DATA:MANAGE")
+ .setContent(mapper.writeValueAsString(regionConfig)));
+
+ // additional credentials needed to create persistent regions
+ regionConfig.setType(RegionType.REPLICATE_PERSISTENT);
+ testContexts.add(new TestContext(post("/v2/regions"), "CLUSTER:WRITE:DISK")
+ .setCredentials("dataManage", "dataManage")
+ .setContent(mapper.writeValueAsString(regionConfig)));
+
+ testContexts.add(new TestContext(get("/v2/regions"), "CLUSTER:READ"));
+ testContexts.add(new TestContext(get("/v2/regions/regionA"), "CLUSTER:READ:regionA"));
+ testContexts.add(new TestContext(delete("/v2/regions/regionA"), "DATA:MANAGE"));
+ testContexts.add(new TestContext(get("/v2/regions/regionA/indexes"), "CLUSTER:READ:QUERY"));
+ testContexts.add(new TestContext(get("/v2/regions/regionA/indexes"), "CLUSTER:READ:QUERY"));
+ testContexts
+ .add(new TestContext(get("/v2/regions/regionA/indexes/index1"), "CLUSTER:READ:QUERY"));
+
+ testContexts.add(new TestContext(get("/v2/gateways/receivers"), "CLUSTER:READ"));
+ testContexts.add(new TestContext(post("/v2/gateways/receivers"), "CLUSTER:MANAGE")
+ .setContent(mapper.writeValueAsString(new GatewayReceiverConfig())));
+
+ testContexts.add(new TestContext(get("/v2/members"), "CLUSTER:READ"));
+ testContexts.add(new TestContext(get("/v2/members/server1"), "CLUSTER:READ"));
+
+ testContexts.add(new TestContext(post("/v2/configurations/pdx"), "CLUSTER:MANAGE")
+ .setContent(mapper.writeValueAsString(new PdxType())));
+ }
+
+ @Before
+ public void before() {
+ context = new LocatorWebContext(webApplicationContext);
+ }
+
+
+ @Test
+ public void notAuthorized() throws Exception {
+ for (TestContext testContext : testContexts) {
+ MockHttpServletRequestBuilder requestBuilder = testContext.request
+ .with(httpBasic(testContext.username, testContext.password));
+ if (testContext.content != null) {
+ requestBuilder.content(testContext.content);
+ }
+ context.perform(requestBuilder)
+ .andExpect(status().isForbidden())
+ .andExpect(jsonPath("$.statusCode", is("UNAUTHORIZED")))
+ .andExpect(jsonPath("$.statusMessage",
+ is(testContext.username + " not authorized for " + testContext.permission)));
+ }
+ }
+
+ @Test
+ public void noCredentials() throws Exception {
+ context.perform(post("/v2/regions"))
+ .andExpect(status().isUnauthorized())
+ .andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
+ .andExpect(jsonPath("$.statusMessage",
+ is("Full authentication is required to access this resource")));
+ }
+
+ @Test
+ public void wrongCredentials() throws Exception {
+ context.perform(post("/v2/regions")
+ .with(httpBasic("user", "wrong_password")))
+ .andExpect(status().isUnauthorized())
+ .andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
+ .andExpect(jsonPath("$.statusMessage",
+ is("Authentication error. Please check your credentials.")));
+ }
+
+ @Test
+ public void successful() throws Exception {
+ RegionConfig regionConfig = new RegionConfig();
+ regionConfig.setName(REGION);
+ regionConfig.setType(RegionType.REPLICATE);
+ context.perform(post("/v2/regions")
+ .with(httpBasic("dataManage", "dataManage"))
+ .content(mapper.writeValueAsString(regionConfig)))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.statusCode", is("OK")))
+ .andExpect(jsonPath("$.statusMessage",
+ is("Successfully updated config for cluster")));
+ }
+
+ private static class TestContext {
+ MockHttpServletRequestBuilder request;
+ String content;
+ String permission;
+ String username = "user";
+ String password = "user";
+
+ public TestContext(MockHttpServletRequestBuilder request, String permission) {
+ this.request = request;
+ this.permission = permission;
+ }
+
+ public TestContext setContent(String content) {
+ this.content = content;
+ return this;
+ }
+
+ public TestContext setCredentials(String username, String password) {
+ this.username = username;
+ this.password = password;
+ return this;
+ }
+ }
+}
diff --git a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java
index e98e203..5d4c520 100644
--- a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java
+++ b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java
@@ -105,6 +105,7 @@ public class RegionManagementController extends AbstractManagementController {
@RequestMapping(method = RequestMethod.GET,
value = REGION_CONFIG_ENDPOINT + "/{regionName}/indexes")
@ResponseBody
+ @PreAuthorize("@securityService.authorize('CLUSTER', 'READ', 'QUERY')")
public ClusterManagementResult<RegionConfig.Index> listIndex(
@PathVariable String regionName,
@RequestParam(required = false) String id) {
@@ -123,6 +124,7 @@ public class RegionManagementController extends AbstractManagementController {
@RequestMapping(method = RequestMethod.GET,
value = REGION_CONFIG_ENDPOINT + "/{regionName}/indexes/{id}")
@ResponseBody
+ @PreAuthorize("@securityService.authorize('CLUSTER', 'READ', 'QUERY')")
public ClusterManagementResult<RegionConfig.Index> getIndex(
@PathVariable String regionName,
@PathVariable String id) {