Skip to content

Commit

Permalink
GEODE-6174: Added rest controller to process post request to /geode-m… (
Browse files Browse the repository at this point in the history
apache#3066)

* GEODE-6174: Added rest controller to process post request to /geode-management/v2/regions

Co-authored-by: Jens Deppe <[email protected]>
Co-authored-by: Peter Tran <[email protected]>

* add geode-web-management module to process the management rest api
* inject security service into the authProvider instead of static lookup
  • Loading branch information
jinmeiliao authored Jan 15, 2019
1 parent 2951e61 commit 8c1629a
Show file tree
Hide file tree
Showing 25 changed files with 1,983 additions and 58 deletions.
4 changes: 4 additions & 0 deletions geode-assembly/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ dependencies {
archives project(':geode-protobuf-messages')
archives project(':geode-web')
archives project(':geode-web-api')
archives project(':geode-web-management')
archives project(':geode-wan')
archives project(':geode-cq')
archives project(':geode-rebalancer')
Expand Down Expand Up @@ -466,6 +467,9 @@ distributions {
from (project(':geode-web-api').configurations.archives.allArtifacts.files) {
exclude '*.jar'
}
from (project(':geode-web-management').configurations.archives.allArtifacts.files) {
exclude '*.jar'
}
}

into('tools/ClientProtocol') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,52 +52,55 @@
public class GeodeDevRestClient {
public static final String CONTEXT = "/geode/v1";

private String context;
private String bindAddress;
private int restPort;
private boolean useSsl;
private HttpHost host;

public GeodeDevRestClient(int restPort) {
this("localhost", restPort, false);
}

public GeodeDevRestClient(String bindAddress, int restPort) {
this(bindAddress, restPort, false);
this(CONTEXT, bindAddress, restPort, false);
}

public GeodeDevRestClient(String bindAddress, int restPort, boolean useSsl) {
this(CONTEXT, bindAddress, restPort, useSsl);
}

public GeodeDevRestClient(String context, String bindAddress, int restPort, boolean useSsl) {
this.context = context;
this.bindAddress = bindAddress;
this.restPort = restPort;
this.useSsl = useSsl;
host = new HttpHost(bindAddress, restPort, useSsl ? "https" : "http");
}

public HttpResponse doHEAD(String query, String username, String password) {
HttpHead httpHead = new HttpHead(CONTEXT + query);
HttpHead httpHead = new HttpHead(context + query);
return doRequest(httpHead, username, password);
}

public HttpResponse doPost(String query, String username, String password, String body) {
HttpPost httpPost = new HttpPost(CONTEXT + query);
HttpPost httpPost = new HttpPost(context + query);
httpPost.addHeader("content-type", "application/json");
httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
return doRequest(httpPost, username, password);
}

public HttpResponse doPut(String query, String username, String password, String body) {
HttpPut httpPut = new HttpPut(CONTEXT + query);
HttpPut httpPut = new HttpPut(context + query);
httpPut.addHeader("content-type", "application/json");
httpPut.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
return doRequest(httpPut, username, password);
}

public HttpResponse doGet(String uri, String username, String password) {
HttpGet getRequest = new HttpGet(CONTEXT + uri);
HttpGet getRequest = new HttpGet(context + uri);
return doRequest(getRequest, username, password);
}

public HttpResponse doDelete(String uri, String username, String password) {
HttpDelete httpDelete = new HttpDelete(CONTEXT + uri);
HttpDelete httpDelete = new HttpDelete(context + uri);
return doRequest(httpDelete, username, password);
}

Expand All @@ -118,6 +121,11 @@ public HttpResponseAssert doPostAndAssert(String uri, String body) {
return new HttpResponseAssert("Post " + uri, doPost(uri, null, null, body));
}

public HttpResponseAssert doPostAndAssert(String uri, String body, String username,
String password) {
return new HttpResponseAssert("Post " + uri, doPost(uri, username, password, body));
}

public HttpResponseAssert doDeleteAndAssert(String uri) {
return new HttpResponseAssert("Delete " + uri, doDelete(uri, null, null));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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;

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.test.junit.rules.GeodeDevRestClient;
import org.apache.geode.test.junit.rules.LocatorStarterRule;
import org.apache.geode.test.junit.rules.RequiresGeodeHome;

public class RegionManagementIntegrationTest {

@ClassRule
public static LocatorStarterRule locator =
new LocatorStarterRule().withHttpService().withAutoStart();

@ClassRule
public static RequiresGeodeHome requiresGeodeHome = new RequiresGeodeHome();

public static GeodeDevRestClient restClient;

@BeforeClass
public static void setUpClass() throws Exception {
restClient =
new GeodeDevRestClient("/geode-management/v2", "localhost", locator.getHttpPort(), false);
}

@Test
public void sanityCheck() throws Exception {
RegionConfig regionConfig = new RegionConfig();
regionConfig.setName("customers");
regionConfig.setRefid("REPLICATE");

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(regionConfig);

restClient.doPostAndAssert("/regions", json, null, null)
.hasStatusCode(201)
.hasResponseBody().isEqualTo("customers");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

import org.apache.geode.cache.configuration.RegionConfig;
import org.apache.geode.security.SimpleTestSecurityManager;
import org.apache.geode.test.junit.rules.GeodeDevRestClient;
import org.apache.geode.test.junit.rules.LocatorStarterRule;
import org.apache.geode.test.junit.rules.RequiresGeodeHome;

public class RegionManagementSecurityIntegrationTest {

@ClassRule
public static LocatorStarterRule locator = new LocatorStarterRule().withHttpService()
.withSecurityManager(SimpleTestSecurityManager.class).withAutoStart();

@ClassRule
public static RequiresGeodeHome requiresGeodeHome = new RequiresGeodeHome();

public static GeodeDevRestClient restClient;

@BeforeClass
public static void setUpClass() {
restClient =
new GeodeDevRestClient("/geode-management/v2", "localhost", locator.getHttpPort(), false);
}

private RegionConfig regionConfig;
private String json;

@Before
public void before() throws JsonProcessingException {
regionConfig = new RegionConfig();
regionConfig.setName("customers");
regionConfig.setRefid("REPLICATE");
ObjectMapper mapper = new ObjectMapper();
json = mapper.writeValueAsString(regionConfig);
}

@Test
public void sanityCheck_not_authorized() throws Exception {
restClient.doPostAndAssert("/regions", json, "test", "test")
.hasStatusCode(403)
.hasResponseBody().isEqualTo("Access is denied");
}

@Test
public void sanityCheckWithNoCredentials() throws Exception {
restClient.doPostAndAssert("/regions", json, null, null)
.hasStatusCode(401);
}

@Test
public void sanityCheckWithWrongCredentials() throws Exception {
restClient.doPostAndAssert("/regions", json, "test", "invalid_pswd")
.hasStatusCode(401);
}

@Test
public void sanityCheck_success() throws Exception {
restClient.doPostAndAssert("/regions", json, "dataManage", "dataManage")
.hasStatusCode(201)
.hasResponseBody().isEqualTo("customers");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ lib/spring-web-4.3.20.RELEASE.jar
tools/ClientProtocol/geode-protobuf-messages-definitions-0.0.0.zip
tools/Extensions/geode-web-0.0.0.war
tools/Extensions/geode-web-api-0.0.0.war
tools/Extensions/geode-web-management-0.0.0.war
tools/Modules/Apache_Geode_Modules-0.0.0-AppServer.zip
tools/Modules/Apache_Geode_Modules-0.0.0-Tomcat.zip
tools/Modules/Apache_Geode_Modules-0.0.0-tcServer.zip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,9 @@ private String lookupWarLocationFromClasspath(String versionedWarFileName,
.findFirst().orElse(null);
}

boolean isWebApplicationAvailable(final String warFileLocation) {
return StringUtils.isNotBlank(warFileLocation);
}

boolean isWebApplicationAvailable(final String... warFileLocations) {
boolean isAnyWarFileAvailable(final String... warFileLocations) {
for (String warFileLocation : warFileLocations) {
if (isWebApplicationAvailable(warFileLocation)) {
if (StringUtils.isNotBlank(warFileLocation)) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,16 @@ private void startHttpService(boolean isServer) {
if (gemfireWar == null) {
if (logger.isDebugEnabled()) {
logger.debug(
"Unable to find GemFire Management REST API WAR file; the Management REST Interface for GemFire will not be accessible.");
"Unable to find GemFire V1 Management REST API WAR file; the Management REST Interface for GemFire will not be accessible.");
}
}

// Find the V2 Management rest WAR file
final String gemfireManagementWar = agentUtil.findWarLocation("geode-web-management");
if (gemfireManagementWar == null) {
if (logger.isDebugEnabled()) {
logger.debug(
"Unable to find GemFire V2 Management REST API WAR file; the Management REST Interface for GemFire will not be accessible.");
}
}

Expand Down Expand Up @@ -248,7 +257,8 @@ private void startHttpService(boolean isServer) {
}

try {
if (agentUtil.isWebApplicationAvailable(gemfireWar, pulseWar, gemfireAPIWar)) {
if (agentUtil.isAnyWarFileAvailable(gemfireWar, gemfireManagementWar, pulseWar,
gemfireAPIWar)) {

final String bindAddress = this.config.getHttpServiceBindAddress();
final int port = this.config.getHttpServicePort();
Expand All @@ -258,20 +268,25 @@ private void startHttpService(boolean isServer) {
this.httpServer = JettyHelper.initJetty(bindAddress, port, SSLConfigurationFactory
.getSSLConfigForComponent(config, SecurableCommunicationChannel.WEB));

if (agentUtil.isWebApplicationAvailable(gemfireWar)) {
if (agentUtil.isAnyWarFileAvailable(gemfireWar)) {
this.httpServer = JettyHelper.addWebApplication(this.httpServer, "/gemfire", gemfireWar,
securityService, null);
this.httpServer = JettyHelper.addWebApplication(this.httpServer, "/geode-mgmt",
gemfireWar, securityService, null);
}

if (agentUtil.isWebApplicationAvailable(pulseWar)) {
if (agentUtil.isAnyWarFileAvailable(gemfireManagementWar)) {
this.httpServer = JettyHelper.addWebApplication(this.httpServer, "/geode-management",
gemfireManagementWar, securityService, null);
}

if (agentUtil.isAnyWarFileAvailable(pulseWar)) {
this.httpServer = JettyHelper.addWebApplication(this.httpServer, "/pulse", pulseWar,
securityService, createSslProps());
}

if (isServer && this.config.getStartDevRestApi()) {
if (agentUtil.isWebApplicationAvailable(gemfireAPIWar)) {
if (agentUtil.isAnyWarFileAvailable(gemfireAPIWar)) {
this.httpServer = JettyHelper.addWebApplication(this.httpServer, "/geode",
gemfireAPIWar, securityService, null);
this.httpServer = JettyHelper.addWebApplication(this.httpServer, "/gemfire-api",
Expand Down Expand Up @@ -307,7 +322,7 @@ private void startHttpService(boolean isServer) {

// now, that Tomcat has been started, we can set the URL used by web
// clients to connect to Pulse
if (agentUtil.isWebApplicationAvailable(pulseWar)) {
if (agentUtil.isAnyWarFileAvailable(pulseWar)) {
managerBean.setPulseURL("http://".concat(getHost(bindAddress)).concat(":")
.concat(String.valueOf(port)).concat("/pulse/"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void startHttpService() {
if (isRunningInTomcat()) {
logger.warn(
"Detected presence of catalina system properties. HTTP service will not be started. To enable the GemFire Developer REST API, please deploy the /geode-web-api WAR file in your application server.");
} else if (agentUtil.isWebApplicationAvailable(gemfireAPIWar)) {
} else if (agentUtil.isAnyWarFileAvailable(gemfireAPIWar)) {

final int port = this.config.getHttpServicePort();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.GenericXmlWebContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.context.web.WebMergedContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;

import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheWriter;
Expand All @@ -67,11 +70,13 @@
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.TimeoutException;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.geode.management.internal.JettyHelper;
import org.apache.geode.management.internal.RestAgent;
import org.apache.geode.test.junit.rules.ServerStarterRule;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:WEB-INF/geode-servlet.xml"})
@ContextConfiguration(locations = {"classpath*:WEB-INF/geode-servlet.xml"},
loader = TestContextLoader.class)
@WebAppConfiguration
public class RestAccessControllerTest {

Expand Down Expand Up @@ -135,8 +140,7 @@ private static void loadResource(String name) throws Exception {

@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.build();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();

rule.getCache().getRegion("customers").clear();
rule.getCache().getRegion("orders").clear();
Expand Down Expand Up @@ -923,5 +927,16 @@ public void beforeRegionClear(RegionEvent event) throws CacheWriterException {
// nothing
}
}
}


class TestContextLoader extends GenericXmlWebContextLoader {
@Override
protected void loadBeanDefinitions(GenericWebApplicationContext context,
WebMergedContextConfiguration webMergedConfig) {
super.loadBeanDefinitions(context, webMergedConfig);
context.getServletContext().setAttribute(JettyHelper.SECURITY_SERVICE_SERVLET_CONTEXT_PARAM,
RestAccessControllerTest.rule.getCache().getSecurityService());
}

}
Loading

0 comments on commit 8c1629a

Please sign in to comment.