/*
* 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.rest.internal.web;
import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_BIND_ADDRESS;
import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_PORT;
import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
import static org.apache.geode.distributed.ConfigurationProperties.START_DEV_REST_API;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.security.TestSecurityManager;
import org.apache.geode.test.dunit.rules.ServerStarterRule;
import org.apache.geode.test.junit.categories.IntegrationTest;
import org.apache.geode.test.junit.categories.SecurityTest;
import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.http.MediaType;
import java.util.Properties;
@Category({IntegrationTest.class, SecurityTest.class})
public class RestSecurityIntegrationTest {
protected static final String REGION_NAME = "AuthRegion";
private static int restPort = AvailablePortHelper.getRandomAvailableTCPPort();
static Properties properties = new Properties() {
{
setProperty(TestSecurityManager.SECURITY_JSON,
"org/apache/geode/management/internal/security/clientServer.json");
setProperty(SECURITY_MANAGER, TestSecurityManager.class.getName());
setProperty(START_DEV_REST_API, "true");
setProperty(HTTP_SERVICE_BIND_ADDRESS, "localhost");
setProperty(HTTP_SERVICE_PORT, restPort + "");
}
};
@ClassRule
public static ServerStarterRule serverStarter = new ServerStarterRule(properties);
private final GeodeRestClient restClient = new GeodeRestClient("localhost", restPort);
@BeforeClass
public static void before() throws Exception {
serverStarter.cache.createRegionFactory(RegionShortcut.REPLICATE).create(REGION_NAME);
}
@Test
public void testFunctions() throws Exception {
String json = "{\"@type\":\"double\",\"@value\":210}";
HttpResponse response = restClient.doGet("/functions", "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
response = restClient.doGet("/functions", "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
response = restClient.doGet("/functions", "dataReader", "1234567");
assertEquals(200, restClient.getCode(response));
response.getEntity();
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
response = restClient.doPost("/functions/AddFreeItemsToOrder", "unknown-user", "1234567", json);
assertEquals(401, restClient.getCode(response));
response = restClient.doPost("/functions/AddFreeItemsToOrder", "dataReader", "1234567", json);
assertEquals(403, restClient.getCode(response));
response = restClient.doPost("/functions/AddFreeItemsToOrder?onRegion=" + REGION_NAME,
"dataWriter", "1234567", json);
// because we're only testing the security of the endpoint, not the endpoint functionality, a
// 500 is acceptable
assertEquals(500, restClient.getCode(response));
}
@Test
public void testQueries() throws Exception {
HttpResponse response = restClient.doGet("/queries", "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
response = restClient.doGet("/queries", "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
response = restClient.doGet("/queries", "dataReader", "1234567");
assertEquals(200, restClient.getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
}
@Test
public void testAdhocQuery() throws Exception {
HttpResponse response = restClient.doGet("/queries/adhoc?q=", "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
response = restClient.doGet("/queries/adhoc?q=", "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
response = restClient.doGet("/queries/adhoc?q=", "dataReader", "1234567");
// because we're only testing the security of the endpoint, not the endpoint functionality, a
// 500 is acceptable
assertEquals(500, restClient.getCode(response));
}
@Test
public void testPostQuery() throws Exception {
HttpResponse response = restClient.doPost("/queries?id=0&q=", "unknown-user", "1234567", "");
assertEquals(401, restClient.getCode(response));
response = restClient.doPost("/queries?id=0&q=", "stranger", "1234567", "");
assertEquals(403, restClient.getCode(response));
response = restClient.doPost("/queries?id=0&q=", "dataReader", "1234567", "");
// because we're only testing the security of the endpoint, not the endpoint functionality, a
// 500 is acceptable
assertEquals(500, restClient.getCode(response));
}
@Test
public void testPostQuery2() throws Exception {
HttpResponse response =
restClient.doPost("/queries/id", "unknown-user", "1234567", "{\"id\" : \"foo\"}");
assertEquals(401, restClient.getCode(response));
response = restClient.doPost("/queries/id", "stranger", "1234567", "{\"id\" : \"foo\"}");
assertEquals(403, restClient.getCode(response));
response = restClient.doPost("/queries/id", "dataReader", "1234567", "{\"id\" : \"foo\"}");
// because we're only testing the security of the endpoint, not the endpoint functionality, a
// 500 is acceptable
assertEquals(500, restClient.getCode(response));
}
@Test
public void testPutQuery() throws Exception {
HttpResponse response =
restClient.doPut("/queries/id", "unknown-user", "1234567", "{\"id\" : \"foo\"}");
assertEquals(401, restClient.getCode(response));
response = restClient.doPut("/queries/id", "stranger", "1234567", "{\"id\" : \"foo\"}");
assertEquals(403, restClient.getCode(response));
response = restClient.doPut("/queries/id", "dataReader", "1234567", "{\"id\" : \"foo\"}");
// We should get a 404 because we're trying to update a query that doesn't exist
assertEquals(404, restClient.getCode(response));
}
@Test
public void testDeleteQuery() throws Exception {
HttpResponse response = restClient.doDelete("/queries/id", "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
response = restClient.doDelete("/queries/id", "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
response = restClient.doDelete("/queries/id", "dataWriter", "1234567");
// We should get a 404 because we're trying to delete a query that doesn't exist
assertEquals(404, restClient.getCode(response));
}
@Test
public void testServers() throws Exception {
HttpResponse response = restClient.doGet("/servers", "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
response = restClient.doGet("/servers", "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
response = restClient.doGet("/servers", "super-user", "1234567");
assertEquals(200, restClient.getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
}
/**
* This test should always return an OK, whether the user is known or unknown. A phishing script
* should not be able to determine whether a user/password combination is good
*/
@Test
public void testPing() throws Exception {
HttpResponse response = restClient.doHEAD("/ping", "stranger", "1234567");
assertEquals(200, restClient.getCode(response));
response = restClient.doGet("/ping", "stranger", "1234567");
assertEquals(200, restClient.getCode(response));
response = restClient.doHEAD("/ping", "super-user", "1234567");
assertEquals(200, restClient.getCode(response));
response = restClient.doGet("/ping", "super-user", "1234567");
assertEquals(200, restClient.getCode(response));
}
/**
* Test permissions on retrieving a list of regions.
*/
@Test
public void getRegions() throws Exception {
HttpResponse response = restClient.doGet("", "dataReader", "1234567");
assertEquals("A '200 - OK' was expected", 200, restClient.getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
JSONObject jsonObject = restClient.getJsonObject(response);
JSONArray regions = jsonObject.getJSONArray("regions");
assertNotNull(regions);
assertTrue(regions.length() > 0);
JSONObject region = regions.getJSONObject(0);
assertEquals("AuthRegion", region.get("name"));
assertEquals("REPLICATE", region.get("type"));
// List regions with an unknown user - 401
response = restClient.doGet("", "unknown-user", "badpassword");
assertEquals(401, restClient.getCode(response));
// list regions with insufficent rights - 403
response = restClient.doGet("", "authRegionReader", "1234567");
assertEquals(403, restClient.getCode(response));
}
/**
* Test permissions on getting a region
*/
@Test
public void getRegion() throws Exception {
// Test an unknown user - 401 error
HttpResponse response = restClient.doGet("/" + REGION_NAME, "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
// Test a user with insufficient rights - 403
response = restClient.doGet("/" + REGION_NAME, "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
// Test an authorized user - 200
response = restClient.doGet("/" + REGION_NAME, "super-user", "1234567");
assertEquals(200, restClient.getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
}
/**
* Test permissions on HEAD region
*/
@Test
public void headRegion() throws Exception {
// Test an unknown user - 401 error
HttpResponse response = restClient.doHEAD("/" + REGION_NAME, "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
// Test a user with insufficient rights - 403
response = restClient.doHEAD("/" + REGION_NAME, "stranger", "1234567");
assertEquals(403, restClient.getCode(response));
// Test an authorized user - 200
response = restClient.doHEAD("/" + REGION_NAME, "super-user", "1234567");
assertEquals(200, restClient.getCode(response));
}
/**
* Test permissions on deleting a region
*/
@Test
public void deleteRegion() throws Exception {
// Test an unknown user - 401 error
HttpResponse response = restClient.doDelete("/" + REGION_NAME, "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
// Test a user with insufficient rights - 403
response = restClient.doDelete("/" + REGION_NAME, "dataReader", "1234567");
assertEquals(403, restClient.getCode(response));
}
/**
* Test permissions on getting a region's keys
*/
@Test
public void getRegionKeys() throws Exception {
// Test an authorized user
HttpResponse response = restClient.doGet("/" + REGION_NAME + "/keys", "super-user", "1234567");
assertEquals(200, restClient.getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
// Test an unauthorized user
response = restClient.doGet("/" + REGION_NAME + "/keys", "dataWriter", "1234567");
assertEquals(403, restClient.getCode(response));
}
/**
* Test permissions on retrieving a key from a region
*/
@Test
public void getRegionKey() throws Exception {
// Test an authorized user
HttpResponse response = restClient.doGet("/" + REGION_NAME + "/key1", "key1User", "1234567");
assertEquals(200, restClient.getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, restClient.getContentType(response));
// Test an unauthorized user
response = restClient.doGet("/" + REGION_NAME + "/key1", "dataWriter", "1234567");
assertEquals(403, restClient.getCode(response));
}
/**
* Test permissions on deleting a region's key(s)
*/
@Test
public void deleteRegionKey() throws Exception {
// Test an unknown user - 401 error
HttpResponse response =
restClient.doDelete("/" + REGION_NAME + "/key1", "unknown-user", "1234567");
assertEquals(401, restClient.getCode(response));
// Test a user with insufficient rights - 403
response = restClient.doDelete("/" + REGION_NAME + "/key1", "dataReader", "1234567");
assertEquals(403, restClient.getCode(response));
// Test an authorized user - 200
response = restClient.doDelete("/" + REGION_NAME + "/key1", "key1User", "1234567");
assertEquals(200, restClient.getCode(response));
}
/**
* Test permissions on deleting a region's key(s)
*/
@Test
public void postRegionKey() throws Exception {
// Test an unknown user - 401 error
HttpResponse response = restClient.doPost("/" + REGION_NAME + "?key9", "unknown", "1234567",
"{ \"key9\" : \"foo\" }");
assertEquals(401, restClient.getCode(response));
// Test a user with insufficient rights - 403
response = restClient.doPost("/" + REGION_NAME + "?key9", "dataReader", "1234567",
"{ \"key9\" : \"foo\" }");
assertEquals(403, restClient.getCode(response));
// Test an authorized user - 200
response = restClient.doPost("/" + REGION_NAME + "?key9", "dataWriter", "1234567",
"{ \"key9\" : \"foo\" }");
assertEquals(201, restClient.getCode(response));
}
/**
* Test permissions on deleting a region's key(s)
*/
@Test
public void putRegionKey() throws Exception {
String json =
"{\"@type\":\"com.gemstone.gemfire.web.rest.domain.Order\",\"purchaseOrderNo\":1121,\"customerId\":1012,\"description\":\"Order for XYZ Corp\",\"orderDate\":\"02/10/2014\",\"deliveryDate\":\"02/20/2014\",\"contact\":\"Jelly Bean\",\"email\":\"jelly.bean@example.com\",\"phone\":\"01-2048096\",\"items\":[{\"itemNo\":1,\"description\":\"Product-100\",\"quantity\":12,\"unitPrice\":5,\"totalPrice\":60}],\"totalPrice\":225}";
String casJSON =
"{\"@old\":{\"@type\":\"com.gemstone.gemfire.web.rest.domain.Order\",\"purchaseOrderNo\":1121,\"customerId\":1012,\"description\":\"Order for XYZ Corp\",\"orderDate\":\"02/10/2014\",\"deliveryDate\":\"02/20/2014\",\"contact\":\"Jelly Bean\",\"email\":\"jelly.bean@example.com\",\"phone\":\"01-2048096\",\"items\":[{\"itemNo\":1,\"description\":\"Product-100\",\"quantity\":12,\"unitPrice\":5,\"totalPrice\":60}],\"totalPrice\":225},\"@new \":{\"@type\":\"com.gemstone.gemfire.web.rest.domain.Order\",\"purchaseOrderNo\":1121,\"customerId\":1013,\"description\":\"Order for New Corp\",\"orderDate\":\"02/10/2014\",\"deliveryDate\":\"02/25/2014\",\"contact\":\"Vanilla Bean\",\"email\":\"vanillabean@example.com\",\"phone\":\"01-2048096\",\"items\":[{\"itemNo\":12345,\"description\":\"part 123\",\"quantity\":12,\"unitPrice\":29.99,\"totalPrice\":149.95}],\"totalPrice\":149.95}}";
// Test an unknown user - 401 error
HttpResponse response = restClient.doPut("/" + REGION_NAME + "/key1?op=PUT", "unknown-user",
"1234567", "{ \"key9\" : \"foo\" }");
assertEquals(401, restClient.getCode(response));
response = restClient.doPut("/" + REGION_NAME + "/key1?op=CAS", "unknown-user", "1234567",
"{ \"key9\" : \"foo\" }");
assertEquals(401, restClient.getCode(response));
response = restClient.doPut("/" + REGION_NAME + "/key1?op=REPLACE", "unknown-user", "1234567",
"{ \"@old\" : \"value1\", \"@new\" : \"CASvalue\" }");
assertEquals(401, restClient.getCode(response));
response = restClient.doPut("/" + REGION_NAME + "/key1?op=PUT", "dataReader", "1234567",
"{ \"key1\" : \"foo\" }");
assertEquals(403, restClient.getCode(response));
response = restClient.doPut("/" + REGION_NAME + "/key1?op=REPLACE", "dataReader", "1234567",
"{ \"key1\" : \"foo\" }");
assertEquals(403, restClient.getCode(response));
response =
restClient.doPut("/" + REGION_NAME + "/key1?op=CAS", "dataReader", "1234567", casJSON);
assertEquals(403, restClient.getCode(response));
response = restClient.doPut("/" + REGION_NAME + "/key1?op=PUT", "key1User", "1234567",
"{ \"key1\" : \"foo\" }");
assertEquals(200, restClient.getCode(response));
response =
restClient.doPut("/" + REGION_NAME + "/key1?op=REPLACE", "key1User", "1234567", json);
assertEquals(200, restClient.getCode(response));
}
}