Skip to content

Commit

Permalink
GEODE-1993: postprocess region/key in developer rest api
Browse files Browse the repository at this point in the history
* This closes #276
  • Loading branch information
Kevin Duling authored and jinmeiliao committed Nov 10, 2016
1 parent e584c4e commit bd229d7
Show file tree
Hide file tree
Showing 19 changed files with 442 additions and 149 deletions.
@@ -0,0 +1,188 @@
/*
* 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.SECURITY_POST_PROCESSOR;
import static org.apache.geode.distributed.ConfigurationProperties.START_DEV_REST_API;
import static org.apache.geode.rest.internal.web.GeodeRestClient.getCode;
import static org.apache.geode.rest.internal.web.GeodeRestClient.getContentType;
import static org.apache.geode.rest.internal.web.GeodeRestClient.getJsonArray;
import static org.apache.geode.rest.internal.web.GeodeRestClient.getJsonObject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.rest.internal.web.controllers.Customer;
import org.apache.geode.rest.internal.web.controllers.RedactingPostProcessor;
import org.apache.geode.security.templates.SampleSecurityManager;
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.net.URLEncoder;
import java.util.Properties;


@Category({IntegrationTest.class, SecurityTest.class})
public class RestSecurityPostProcessorTest {

static int restPort = AvailablePortHelper.getRandomAvailableTCPPort();
static Properties properties = new Properties() {
{
setProperty(SampleSecurityManager.SECURITY_JSON,
"org/apache/geode/management/internal/security/clientServer.json");
setProperty(SECURITY_MANAGER, SampleSecurityManager.class.getName());
setProperty(START_DEV_REST_API, "true");
setProperty(HTTP_SERVICE_BIND_ADDRESS, "localhost");
setProperty(HTTP_SERVICE_PORT, restPort + "");
setProperty(SECURITY_POST_PROCESSOR, RedactingPostProcessor.class.getName());
}
};

@ClassRule
public static ServerStarterRule serverStarter = new ServerStarterRule(properties);
private final GeodeRestClient restClient = new GeodeRestClient("localhost", restPort);

@BeforeClass
public static void before() throws Exception {
Region region =
serverStarter.cache.createRegionFactory(RegionShortcut.REPLICATE).create("customers");
region.put("1", new Customer(1L, "John", "Doe", "555555555"));
region.put("2", new Customer(2L, "Richard", "Roe", "222533554"));
region.put("3", new Customer(3L, "Jane", "Doe", "555223333"));
region.put("4", new Customer(4L, "Jane", "Roe", "555443333"));
}

/**
* Test post-processing of a retrieved key from the server.
*/
@Test
public void getRegionKey() throws Exception {
// Test a single key
HttpResponse response = restClient.doGet("/customers/1", "dataReader", "1234567");
assertEquals(200, getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, getContentType(response));

// Ensure SSN is hidden
JSONObject jsonObject = getJsonObject(response);
assertEquals("*********", jsonObject.getString("socialSecurityNumber"));
assertEquals(1L, jsonObject.getLong("customerId"));

// Try with super-user
response = restClient.doGet("/customers/1", "super-user", "1234567");
assertEquals(200, getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, getContentType(response));

// ensure SSN is readable
jsonObject = getJsonObject(response);
assertEquals("555555555", jsonObject.getString("socialSecurityNumber"));
assertEquals(1L, jsonObject.getLong("customerId"));
}

// Test multiple keys
@Test
public void getMultipleRegionKeys() throws Exception {
HttpResponse response = restClient.doGet("/customers/1,3", "dataReader", "1234567");
assertEquals(200, getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, getContentType(response));

JSONObject jsonObject = getJsonObject(response);
JSONArray jsonArray = jsonObject.getJSONArray("customers");
final int length = jsonArray.length();
assertEquals(2, length);
JSONObject customer = jsonArray.getJSONObject(0);
assertEquals("*********", customer.getString("socialSecurityNumber"));
assertEquals(1, customer.getLong("customerId"));
customer = jsonArray.getJSONObject(1);
assertEquals("*********", customer.getString("socialSecurityNumber"));
assertEquals(3, customer.getLong("customerId"));
}

@Test
public void getRegion() throws Exception {
HttpResponse response = restClient.doGet("/customers", "dataReader", "1234567");
assertEquals(200, getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, getContentType(response));

JSONObject jsonObject = getJsonObject(response);
JSONArray jsonArray = jsonObject.getJSONArray("customers");
final int length = jsonArray.length();
for (int index = 0; index < length; ++index) {
JSONObject customer = jsonArray.getJSONObject(index);
assertEquals("*********", customer.getString("socialSecurityNumber"));
assertEquals((long) index + 1, customer.getLong("customerId"));
}
}

@Test
public void adhocQuery() throws Exception {
String query = "/queries/adhoc?q="
+ URLEncoder.encode("SELECT * FROM /customers order by customerId", "UTF-8");
HttpResponse response = restClient.doGet(query, "dataReader", "1234567");
assertEquals(200, getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, getContentType(response));

JSONArray jsonArray = getJsonArray(response);
final int length = jsonArray.length();
for (int index = 0; index < length; ++index) {
JSONObject customer = jsonArray.getJSONObject(index);
assertEquals("*********", customer.getString("socialSecurityNumber"));
assertEquals((long) index + 1, customer.getLong("customerId"));
}
}

@Test
public void namedQuery() throws Exception {
// Declare the named query
String namedQuery = "SELECT c FROM /customers c WHERE c.customerId = $1";

// Install the named query
HttpResponse response =
restClient.doPost("/queries?id=selectCustomer&q=" + URLEncoder.encode(namedQuery, "UTF-8"),
"dataReader", "1234567", "");
assertEquals(201, getCode(response));

// Verify the query has been installed
String query = "/queries";
response = restClient.doGet(query, "dataReader", "1234567");
assertEquals(200, getCode(response));
assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE, getContentType(response));

// Execute the query
response = restClient.doPost("/queries/selectCustomer", "dataReader", "1234567",
"{" + "\"@type\": \"int\"," + "\"@value\": 1" + "}");
assertEquals(200, getCode(response));

// Validate the result
JSONArray jsonArray = getJsonArray(response);
assertTrue(jsonArray.length() == 1);
JSONObject customer = jsonArray.getJSONObject(0);
assertEquals("*********", customer.getString("socialSecurityNumber"));
assertEquals(1L, customer.getLong("customerId"));
}
}
Expand Up @@ -14,32 +14,40 @@
*/
package org.apache.geode.rest.internal.web.controllers;

import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.geode.internal.lang.ObjectUtils;

import java.io.Serializable;

/**
* The Customer class models a customer entity.
* <p/>
*
* @since GemFire 8.0
*/

@JsonInclude(Include.NON_NULL)
public class Customer implements Serializable {

@JsonProperty("id")
private Long customerId;
private String firstName;
private String lastName;
@JsonProperty("ssn")
private String socialSecurityNumber;

public Customer() {}

public Customer(final Long custId) {
this.customerId = custId;
}

public Customer(final Long custId, final String fname, final String lname) {
public Customer(final Long custId, final String fname, final String lname, final String ssn) {
this.customerId = custId;
this.firstName = fname;
this.lastName = lname;
this.socialSecurityNumber = ssn;
}

public Long getCustomerId() {
Expand Down Expand Up @@ -80,7 +88,16 @@ public boolean equals(final Object obj) {

return (ObjectUtils.equals(this.getCustomerId(), that.getCustomerId())
&& ObjectUtils.equals(this.getLastName(), that.getLastName())
&& ObjectUtils.equals(this.getFirstName(), that.getFirstName()));
&& ObjectUtils.equals(this.getFirstName(), that.getFirstName())
&& ObjectUtils.equals(this.getSocialSecurityNumber(), that.getSocialSecurityNumber()));
}

public String getSocialSecurityNumber() {
return socialSecurityNumber;
}

public void setSocialSecurityNumber(final String ssn) {
this.socialSecurityNumber = ssn;
}

@Override
Expand All @@ -99,6 +116,7 @@ public String toString() {
buffer.append(", customerId = ").append(getCustomerId());
buffer.append(", firstName = ").append(getFirstName());
buffer.append(", lastName = ").append(getLastName());
buffer.append(", ssn = ").append(getSocialSecurityNumber());
buffer.append(" }");
return buffer.toString();
}
Expand Down
@@ -0,0 +1,67 @@
/*
* 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.controllers;

import org.apache.geode.security.PostProcessor;

import java.security.Principal;
import java.util.Properties;

/**
* This is example that implements PostProcessor that will redact Social Security Numbers from
* Customer objects
*/
public class RedactingPostProcessor implements PostProcessor {

@Override
public void init(final Properties securityProps) {}

/**
* Protect the Customer's Social Security Number from unauthorized users
*
* @param principal The principal that's accessing the value
* @param regionName The region that's been accessed. This could be null.
* @param id the customer id the lookup was done with. This could be null.
* @param customer a @Customer object, this could be null.
* @return the processed value
*/
@Override
public Object processRegionValue(Object principal, String regionName, Object id,
Object customer) {
if (customer == null)
return null;
if (customer instanceof Customer) {
String username = getUsername(principal);
// Unable to retrieve the role at this point, so for this demo we'll just work with the
// username
if (username.equals("super-user"))
return customer;
Customer cust = (Customer) customer;
return new Customer(cust.getCustomerId(), cust.getFirstName(), cust.getLastName(),
"*********");
} else
return customer;
}

private String getUsername(final Object principal) {
String name = null;
if (principal instanceof Principal) {
name = ((Principal) principal).getName();
} else {
name = principal.toString();
}
return name;
}
}

0 comments on commit bd229d7

Please sign in to comment.