blob: ec016abd8f3b4f322d48450cf1fa8c4b0537fe66 [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.security;
import static org.apache.geode.security.SecurityTestUtils.closeCache;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.DataSerializable;
import org.apache.geode.Instantiator;
import org.apache.geode.cache.operations.OperationContext.OperationCode;
import org.apache.geode.distributed.ConfigurationProperties;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.security.generator.CredentialGenerator;
import org.apache.geode.security.generator.DummyAuthzCredentialGenerator;
import org.apache.geode.security.generator.DummyCredentialGenerator;
import org.apache.geode.security.templates.UserPasswordAuthInit;
import org.apache.geode.test.junit.categories.SecurityTest;
/**
* Tests for authorization callback that modify objects and callbacks from client to server.
*
* The scheme of these tests is thus: A user name terminating in integer 'i' can get values
* terminating with 'i', '2*i', '3*i' and so on. So 'gemfire1' can get 'value1', 'value2', ...;
* 'gemfire2' can get 'value2', 'value4', ... and so on. On the server side this is done by adding
* the index 'i' to the object in the pre-processing phase, and checked by comparing against the
* user name index during the post-processing phase.
*
* This enables testing of object and callback modification both in pre-processing and
* post-processing phases.
*
* @since GemFire 5.5
*/
@Category({SecurityTest.class})
public class ClientAuthzObjectModDUnitTest extends ClientAuthorizationTestCase {
private static final String PRE_ACCESSOR_CREATE =
FilterPreAuthorization.class.getName() + ".create";
private static final String POST_ACCESSOR_CREATE =
FilterPostAuthorization.class.getName() + ".create";
@Override
protected final void postSetUpClientAuthorizationTestBase() throws Exception {
// required by FilterPreAuthorization and FilterPostAuthorization. Normally,
// this would be automatically registered in the static initializer, but with dunit
// a previous test may have already loaded these classes. We clear the instantiators
// between each test.
server1.invoke("registerInstantiator",
() -> Instantiator.register(new MyInstantiator(), false));
server2.invoke("registerInstantiator",
() -> Instantiator.register(new MyInstantiator(), false));
}
@Test
public void testAllOpsObjectModWithFailover() throws Exception {
OperationWithAction[] allOps = allOps();
TestPostCredentialGenerator tgen = new TestPostCredentialGenerator();
CredentialGenerator credentialGenerator = new DummyCredentialGenerator();
credentialGenerator.init();
Properties extraProps = credentialGenerator.getSystemProperties();
Properties javaProps = credentialGenerator.getJavaProperties();
String authInit = credentialGenerator.getAuthInit();
String authenticator = credentialGenerator.getAuthenticator();
System.out.println("testPutsGetsObjectModWithFailover: Using authinit: " + authInit);
System.out.println("testPutsGetsObjectModWithFailover: Using authenticator: " + authenticator);
System.out.println(
"testPutsGetsObjectModWithFailover: Using pre-operation accessor: " + PRE_ACCESSOR_CREATE);
System.out.println("testPutsGetsObjectModWithFailover: Using post-operation accessor: "
+ POST_ACCESSOR_CREATE);
// Start servers with all required properties
Properties serverProps =
buildProperties(authenticator, extraProps, PRE_ACCESSOR_CREATE, POST_ACCESSOR_CREATE);
// Get ports for the servers
int[] portsList = AvailablePortHelper.getRandomAvailableTCPPorts(2);
int port1 = 0; // portsList[0];
int port2 = portsList[1];
// Perform all the ops on the clients
List<OperationWithAction> opBlock = new ArrayList();
Random rnd = new Random();
for (int opNum = 0; opNum < allOps.length; ++opNum) {
// Start client with valid credentials as specified in OperationWithAction
OperationWithAction currentOp = allOps[opNum];
if (currentOp.equals(OperationWithAction.OPBLOCK_END)
|| currentOp.equals(OperationWithAction.OPBLOCK_NO_FAILOVER)) {
// End of current operation block; execute all the operations on the servers with failover
if (opBlock.size() > 0) {
// Start the first server and execute the operation block
port1 = server1.invoke("createCacheServer",
() -> createCacheServer(serverProps, javaProps));
server2.invoke("closeCache", () -> closeCache());
executeOpBlock(opBlock, port1, port2, authInit, extraProps, null, tgen, rnd);
if (!currentOp.equals(OperationWithAction.OPBLOCK_NO_FAILOVER)) {
// Failover to the second server and run the block again
port2 = server2.invoke("createCacheServer",
() -> createCacheServer(serverProps, javaProps));
server1.invoke("closeCache", () -> closeCache());
executeOpBlock(opBlock, port1, port2, authInit, extraProps, null, tgen, rnd);
}
opBlock.clear();
}
} else {
currentOp.setOpNum(opNum);
opBlock.add(currentOp);
}
}
}
private OperationWithAction[] allOps() {
return new OperationWithAction[] {
// Perform CREATE and verify with GET
new OperationWithAction(OperationCode.PUT, 1, OpFlags.NONE, 8),
// For second client connect with valid credentials for key2, key4, key6, key8 and check
// that other KEYS are not accessible
new OperationWithAction(OperationCode.GET, 2, OpFlags.CHECK_NOKEY, new int[] {1, 3, 5, 7}),
new OperationWithAction(OperationCode.GET, 2,
OpFlags.CHECK_NOKEY | OpFlags.USE_OLDCONN | OpFlags.CHECK_NOTAUTHZ,
new int[] {0, 2, 4, 6}),
// For third client check that key3, key6 are accessible but others are not
new OperationWithAction(OperationCode.GET, 3, OpFlags.CHECK_NOKEY, new int[] {2, 5}),
new OperationWithAction(OperationCode.GET, 3,
OpFlags.CHECK_NOKEY | OpFlags.USE_OLDCONN | OpFlags.CHECK_NOTAUTHZ,
new int[] {0, 1, 3, 4, 6, 7}),
// OPBLOCK_END indicates end of an operation block that needs to be executed on each server
// when doing failover
OperationWithAction.OPBLOCK_END,
// Perform UPDATE and verify with GET
new OperationWithAction(OperationCode.PUT, 1, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL, 8),
// For second client check that key2, key4, key6, key8 are accessible but others are not
new OperationWithAction(OperationCode.GET, 2, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL,
new int[] {1, 3, 5, 7}),
new OperationWithAction(OperationCode.GET, 2,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.CHECK_NOKEY | OpFlags.CHECK_NOTAUTHZ,
new int[] {0, 2, 4, 6}),
// For third client check that key3, key6 are accessible but others are not
new OperationWithAction(OperationCode.GET, 3, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL,
new int[] {2, 5}),
new OperationWithAction(OperationCode.GET, 3,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.CHECK_NOKEY | OpFlags.CHECK_NOTAUTHZ,
new int[] {0, 1, 3, 4, 6, 7}),
OperationWithAction.OPBLOCK_END,
// Perform UPDATE and verify with GET_ALL
new OperationWithAction(OperationCode.PUT, 1, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL, 8),
// For second client check that key2, key4, key6, key8 are accessible but others are not;
// getAll test in doOp uses a combination of local entries and remote fetches
new OperationWithAction(OperationCode.GET, 2,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.USE_ALL_KEYS,
new int[] {1, 3, 5, 7}),
new OperationWithAction(OperationCode.GET, 2,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.USE_ALL_KEYS | OpFlags.CHECK_NOKEY
| OpFlags.CHECK_FAIL,
new int[] {0, 2, 4, 6}),
// For third client check that key3, key6 are accessible but others are not
new OperationWithAction(OperationCode.GET, 3,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.USE_ALL_KEYS, new int[] {2, 5}),
new OperationWithAction(OperationCode.GET, 3,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.USE_ALL_KEYS | OpFlags.CHECK_NOKEY
| OpFlags.CHECK_FAIL,
new int[] {0, 1, 3, 4, 6, 7}),
// locally destroy the KEYS to also test create after failover
new OperationWithAction(OperationCode.DESTROY, 1, OpFlags.USE_OLDCONN | OpFlags.LOCAL_OP,
8),
OperationWithAction.OPBLOCK_END,
// Perform PUTALL and verify with GET
new OperationWithAction(OperationCode.PUTALL, 1, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL,
8),
// For second client check that key2, key4, key6, key8 are accessible but others are not
new OperationWithAction(OperationCode.GET, 2, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL,
new int[] {1, 3, 5, 7}),
new OperationWithAction(OperationCode.GET, 2,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.CHECK_NOKEY | OpFlags.CHECK_NOTAUTHZ,
new int[] {0, 2, 4, 6}),
// For third client check that key3, key6 are accessible but others are not
new OperationWithAction(OperationCode.GET, 3, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL,
new int[] {2, 5}),
new OperationWithAction(OperationCode.GET, 3,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.CHECK_NOKEY | OpFlags.CHECK_NOTAUTHZ,
new int[] {0, 1, 3, 4, 6, 7}),
OperationWithAction.OPBLOCK_END,
// Test UPDATE and verify with a QUERY
new OperationWithAction(OperationCode.PUT, 1, OpFlags.USE_OLDCONN, 8),
// For second client check that key2, key4, key6, key8 are accessible but others are not
new OperationWithAction(OperationCode.QUERY, 2, OpFlags.USE_OLDCONN,
new int[] {1, 3, 5, 7}),
new OperationWithAction(OperationCode.QUERY, 2, OpFlags.USE_OLDCONN | OpFlags.CHECK_FAIL,
new int[] {0, 2, 4, 6}),
// For third client check that key3, key6 are accessible but others are not
new OperationWithAction(OperationCode.QUERY, 3, OpFlags.USE_OLDCONN, new int[] {2, 5}),
new OperationWithAction(OperationCode.QUERY, 3, OpFlags.USE_OLDCONN | OpFlags.CHECK_FAIL,
new int[] {0, 1, 3, 4, 6, 7}),
OperationWithAction.OPBLOCK_END,
// Test UPDATE and verify with a EXECUTE_CQ initial results
new OperationWithAction(OperationCode.PUT, 1, OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL, 8),
// For second client check that key2, key4, key6, key8 are accessible but others are not
new OperationWithAction(OperationCode.EXECUTE_CQ, 2,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL, new int[] {1, 3, 5, 7}),
new OperationWithAction(OperationCode.CLOSE_CQ, 2, OpFlags.USE_OLDCONN, 1),
new OperationWithAction(OperationCode.EXECUTE_CQ, 2,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.CHECK_FAIL, new int[] {0, 2, 4, 6}),
new OperationWithAction(OperationCode.CLOSE_CQ, 2, OpFlags.USE_OLDCONN, 1),
// For third client check that key3, key6 are accessible but others are not
new OperationWithAction(OperationCode.EXECUTE_CQ, 3,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL, new int[] {2, 5}),
new OperationWithAction(OperationCode.CLOSE_CQ, 3, OpFlags.USE_OLDCONN, 1),
new OperationWithAction(OperationCode.EXECUTE_CQ, 3,
OpFlags.USE_OLDCONN | OpFlags.USE_NEWVAL | OpFlags.CHECK_FAIL,
new int[] {0, 1, 3, 4, 6, 7}),
new OperationWithAction(OperationCode.CLOSE_CQ, 3, OpFlags.USE_OLDCONN, 1),
OperationWithAction.OPBLOCK_END};
}
private Properties buildProperties(final String authenticator, final Properties extraProps,
final String preAccessor, final String postAccessor) {
Properties authProps = new Properties();
if (authenticator != null) {
authProps.setProperty(ConfigurationProperties.SECURITY_CLIENT_AUTHENTICATOR, authenticator);
}
if (preAccessor != null) {
authProps.setProperty(ConfigurationProperties.SECURITY_CLIENT_ACCESSOR, preAccessor);
}
if (postAccessor != null) {
authProps.setProperty(ConfigurationProperties.SECURITY_CLIENT_ACCESSOR_PP, postAccessor);
}
if (extraProps != null) {
authProps.putAll(extraProps);
}
return authProps;
}
private static class MyInstantiator extends Instantiator {
public MyInstantiator() {
this(ObjectWithAuthz.class, ObjectWithAuthz.CLASSID);
}
public MyInstantiator(final Class clazz, final int classId) {
super(clazz, classId);
}
@Override
public DataSerializable newInstance() {
return new ObjectWithAuthz();
}
}
private static class TestPostCredentialGenerator implements TestCredentialGenerator {
public TestPostCredentialGenerator() {}
@Override
public Properties getAllowedCredentials(final OperationCode[] opCodes,
final String[] regionNames, final int[] keyIndices, final int num) {
int userIndex = 1;
byte role = DummyAuthzCredentialGenerator.getRequiredRole(opCodes);
if (role == DummyAuthzCredentialGenerator.READER_ROLE) {
userIndex = keyIndices[0] + 1;
}
Properties props = new Properties();
props.setProperty(UserPasswordAuthInit.USER_NAME, "user" + userIndex);
props.setProperty(UserPasswordAuthInit.PASSWORD, "user" + userIndex);
return props;
}
@Override
public Properties getDisallowedCredentials(final OperationCode[] opCodes,
final String[] regionNames, final int[] keyIndices, final int num) {
int userIndex = 0;
for (int index = 0; index < keyIndices.length; ++index) {
if (keyIndices[index] != index) {
userIndex = index + 1;
break;
}
}
Properties props = new Properties();
props.setProperty(UserPasswordAuthInit.USER_NAME, "gemfire" + userIndex);
props.setProperty(UserPasswordAuthInit.PASSWORD, "gemfire" + userIndex);
return props;
}
@Override
public CredentialGenerator getCredentialGenerator() {
return null;
}
}
}