blob: 48564da553894c5cdb80d489a019fdba872b0c4f [file] [log] [blame]
/*
* 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.request.MockMvcRequestBuilders.put;
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.annotation.DirtiesContext;
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.management.configuration.Index;
import org.apache.geode.management.configuration.RegionType;
import org.apache.geode.management.operation.RebalanceOperation;
import org.apache.geode.util.internal.GeodeJsonMapper;
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath*:WEB-INF/management-servlet.xml"},
loader = SecuredLocatorContextLoader.class)
@WebAppConfiguration
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class ClusterManagementSecurityRestIntegrationTest {
private static final String REGION = "products";
@Autowired
private WebApplicationContext webApplicationContext;
private LocatorWebContext context;
private static final 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("/v1/regions"), "DATA:MANAGE")
.setContent(mapper.writeValueAsString(regionConfig)));
// additional credentials needed to create persistent regions
regionConfig.setType(RegionType.REPLICATE_PERSISTENT);
testContexts.add(new TestContext(post("/v1/regions"), "CLUSTER:WRITE:DISK")
.setCredentials("dataManage", "dataManage")
.setContent(mapper.writeValueAsString(regionConfig)));
testContexts.add(new TestContext(get("/v1/regions"), "CLUSTER:READ"));
testContexts.add(new TestContext(get("/v1/regions/regionA"), "CLUSTER:READ:regionA"));
testContexts.add(new TestContext(delete("/v1/regions/regionA"), "DATA:MANAGE"));
testContexts
.add(new TestContext(get("/v1/regions/regionA/indexes"), "CLUSTER:READ:QUERY"));
testContexts
.add(new TestContext(get("/v1/regions/regionA/indexes"), "CLUSTER:READ:QUERY"));
testContexts
.add(new TestContext(get("/v1/regions/regionA/indexes/index1"),
"CLUSTER:READ:QUERY"));
testContexts
.add(new TestContext(post("/v1/regions/regionA/indexes/"),
"CLUSTER:MANAGE:QUERY").setContent(mapper.writeValueAsString(new Index())));
testContexts
.add(new TestContext(delete("/v1/regions/regionA/indexes/index1"),
"CLUSTER:MANAGE:QUERY"));
testContexts.add(new TestContext(get("/v1/gateways/receivers"), "CLUSTER:READ"));
testContexts
.add(new TestContext(get("/v1/gateways/receivers/receiver1"), "CLUSTER:READ"));
testContexts.add(new TestContext(post("/v1/gateways/receivers"), "CLUSTER:MANAGE")
.setContent(mapper.writeValueAsString(new GatewayReceiverConfig())));
testContexts.add(new TestContext(get("/v1/members"), "CLUSTER:READ"));
testContexts.add(new TestContext(get("/v1/members/server1"), "CLUSTER:READ"));
testContexts.add(new TestContext(post("/v1/configurations/pdx"), "CLUSTER:MANAGE")
.setContent(mapper.writeValueAsString(new PdxType())));
testContexts.add(new TestContext(put("/v1/configurations/pdx"), "CLUSTER:MANAGE")
.setContent(mapper.writeValueAsString(new PdxType())));
testContexts.add(new TestContext(delete("/v1/configurations/pdx"), "CLUSTER:MANAGE")
.setContent(mapper.writeValueAsString(new PdxType())));
testContexts.add(new TestContext(get("/v1/configurations/pdx"), "CLUSTER:READ"));
testContexts.add(new TestContext(post("/v1/operations/rebalances"), "DATA:MANAGE")
.setContent(mapper.writeValueAsString(new RebalanceOperation())));
testContexts
.add(new TestContext(get("/v1/operations/rebalances/123"), "DATA:MANAGE"));
}
@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(sentenceCase(testContext.username) + " not authorized for "
+ testContext.permission + ".")));
}
}
private static String sentenceCase(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
@Test
public void noCredentials() throws Exception {
context.perform(post("/v1/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("/v1/regions")
.with(httpBasic("user", "wrong_password")))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.statusCode", is("UNAUTHENTICATED")))
.andExpect(jsonPath("$.statusMessage",
is("Invalid username/password.")));
}
@Test
public void successful() throws Exception {
RegionConfig regionConfig = new RegionConfig();
regionConfig.setName(REGION);
regionConfig.setType(RegionType.REPLICATE);
context.perform(post("/v1/regions")
.with(httpBasic("dataManage", "dataManage"))
.content(mapper.writeValueAsString(regionConfig)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.statusCode", is("OK")))
.andExpect(jsonPath("$.statusMessage",
is("Successfully updated configuration for cluster.")));
// cleanup in order to pass stressNew
context.perform(delete("/v1/regions/" + REGION)
.with(httpBasic("dataManage", "dataManage")))
.andExpect(status().is2xxSuccessful());
}
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;
}
}
}