blob: cd0b05732ce2b61e91796d324442cddc24a709e0 [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.cli.commands;
import static org.apache.geode.management.internal.cli.commands.AlterQueryServiceCommand.AUTHORIZER_NAME;
import static org.apache.geode.management.internal.cli.commands.AlterQueryServiceCommand.AUTHORIZER_PARAMETERS;
import static org.apache.geode.management.internal.cli.commands.AlterQueryServiceCommand.COMMAND_NAME;
import static org.apache.geode.management.internal.cli.commands.AlterQueryServiceCommand.NO_MEMBERS_FOUND_MESSAGE;
import static org.apache.geode.management.internal.cli.commands.AlterQueryServiceCommand.SPLITTING_REGEX;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.IntStream;
import junitparams.JUnitParamsRunner;
import org.apache.commons.io.FileUtils;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.apache.geode.cache.query.internal.QueryConfigurationService;
import org.apache.geode.cache.query.security.JavaBeanAccessorMethodAuthorizer;
import org.apache.geode.cache.query.security.MethodInvocationAuthorizer;
import org.apache.geode.cache.query.security.RegExMethodAuthorizer;
import org.apache.geode.cache.query.security.RestrictedMethodAuthorizer;
import org.apache.geode.cache.query.security.UnrestrictedMethodAuthorizer;
import org.apache.geode.examples.SimpleSecurityManager;
import org.apache.geode.management.internal.cli.util.TestMethodAuthorizer;
import org.apache.geode.test.compiler.ClassBuilder;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.MemberVM;
import org.apache.geode.test.junit.rules.GfshCommandRule;
import org.apache.geode.test.junit.rules.VMProvider;
@RunWith(JUnitParamsRunner.class)
public class AlterQueryServiceCommandWithSecurityDUnitTest {
private static final Class<RestrictedMethodAuthorizer> DEFAULT_AUTHORIZER_CLASS =
RestrictedMethodAuthorizer.class;
private static final String TEST_AUTHORIZER_TXT = "TestMethodAuthorizer.txt";
private static final String USER_AUTHORIZER_PARAMETERS = "param1;param2;param3";
private static final String JAVA_BEAN_AUTHORIZER_PARAMETERS = "java.lang;java.util";
// A regex containing a comma to confirm that '--authorizer-parameter' is correctly parsed
private static final String REGEX_AUTHORIZER_PARAMETERS =
"^java.util.List..{4,8}$;^java.util.Set..{4,8}$";
private MemberVM locator;
private List<MemberVM> servers = new ArrayList<>();
@Rule
public GfshCommandRule gfsh = new GfshCommandRule();
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Rule
public ClusterStartupRule cluster = new ClusterStartupRule();
@Before
public void setUp() throws Exception {
locator = cluster.startLocatorVM(0, l -> l.withSecurityManager(SimpleSecurityManager.class));
int locatorPort = locator.getPort();
IntStream.range(0, 2).forEach(i -> servers.add(i, cluster.startServerVM(i + 1, s -> s
.withConnectionToLocator(locatorPort).withCredential("clusterManage", "clusterManage"))));
gfsh.connectAndVerify(locator);
}
private String buildCommand(String authorizerName, String authorizerParameters) {
StringBuilder commandBuilder = new StringBuilder(COMMAND_NAME);
commandBuilder.append(" --").append(AUTHORIZER_NAME).append("=").append(authorizerName);
if (authorizerParameters != null) {
commandBuilder.append(" --").append(AUTHORIZER_PARAMETERS).append("=")
.append(authorizerParameters);
}
return commandBuilder.toString();
}
private void executeAndAssertGfshCommandIsSuccessful(
Class<? extends MethodInvocationAuthorizer> authorizerClass,
String authorizerParameters) {
gfsh.executeAndAssertThat(buildCommand(authorizerClass.getName(), authorizerParameters))
.statusIsSuccess().hasTableSection().hasRowSize(servers.size());
verifyCurrentAuthorizerClass(authorizerClass);
}
private void startServerAndVerifyJavaBeanAccessorMethodAuthorizer() {
int locatorPort = locator.getPort();
MemberVM newServer = cluster.startServerVM(servers.size() + 1, s -> s
.withConnectionToLocator(locatorPort).withCredential("clusterManage", "clusterManage"));
newServer.invoke(() -> {
JavaBeanAccessorMethodAuthorizer methodAuthorizer =
(JavaBeanAccessorMethodAuthorizer) Objects.requireNonNull(ClusterStartupRule.getCache())
.getService(QueryConfigurationService.class).getMethodAuthorizer();
assertThat(methodAuthorizer.getClass()).isEqualTo(JavaBeanAccessorMethodAuthorizer.class);
Set<String> expectedParameters =
new HashSet<>(Arrays.asList(JAVA_BEAN_AUTHORIZER_PARAMETERS.split(SPLITTING_REGEX)));
assertThat(methodAuthorizer.getAllowedPackages()).isEqualTo(expectedParameters);
});
}
private void verifyCurrentAuthorizerClass(
Class<? extends MethodInvocationAuthorizer> authorizerClass) {
servers.forEach(server -> server.invoke(() -> {
MethodInvocationAuthorizer methodAuthorizer =
Objects.requireNonNull(ClusterStartupRule.getCache())
.getService(QueryConfigurationService.class).getMethodAuthorizer();
assertThat(methodAuthorizer.getClass()).isEqualTo(authorizerClass);
}));
}
private String getTestMethodAuthorizerFilePath() throws IOException {
URL url = getClass().getResource(TEST_AUTHORIZER_TXT);
File textFile = this.tempFolder.newFile(TEST_AUTHORIZER_TXT);
FileUtils.copyURLToFile(url, textFile);
return textFile.getAbsolutePath();
}
// This test verifies that the cluster starts with the default method authorizer configured, then
// changes the currently configured authorizer to each of the out-of-the-box authorizers.
@Test
public void alterQueryServiceCommandUpdatesMethodAuthorizerClass() {
verifyCurrentAuthorizerClass(DEFAULT_AUTHORIZER_CLASS);
executeAndAssertGfshCommandIsSuccessful(UnrestrictedMethodAuthorizer.class, "");
executeAndAssertGfshCommandIsSuccessful(JavaBeanAccessorMethodAuthorizer.class,
JAVA_BEAN_AUTHORIZER_PARAMETERS);
executeAndAssertGfshCommandIsSuccessful(RegExMethodAuthorizer.class,
REGEX_AUTHORIZER_PARAMETERS);
executeAndAssertGfshCommandIsSuccessful(RestrictedMethodAuthorizer.class, "");
}
@Test
public void alterQueryServiceCommandUpdatesMethodAuthorizerParameters() {
gfsh.executeAndAssertThat(buildCommand(JavaBeanAccessorMethodAuthorizer.class.getName(),
JAVA_BEAN_AUTHORIZER_PARAMETERS)).statusIsSuccess().hasTableSection()
.hasRowSize(servers.size());
servers.forEach(server -> server.invoke(() -> {
JavaBeanAccessorMethodAuthorizer methodAuthorizer =
(JavaBeanAccessorMethodAuthorizer) Objects.requireNonNull(ClusterStartupRule.getCache())
.getService(QueryConfigurationService.class).getMethodAuthorizer();
Set<String> expectedParameters =
new HashSet<>(Arrays.asList(JAVA_BEAN_AUTHORIZER_PARAMETERS.split(SPLITTING_REGEX)));
assertThat(methodAuthorizer.getAllowedPackages()).isEqualTo(expectedParameters);
}));
gfsh.executeAndAssertThat(
buildCommand(RegExMethodAuthorizer.class.getName(), REGEX_AUTHORIZER_PARAMETERS))
.statusIsSuccess().hasTableSection().hasRowSize(servers.size());
servers.forEach(server -> server.invoke(() -> {
RegExMethodAuthorizer methodAuthorizer =
(RegExMethodAuthorizer) Objects.requireNonNull(ClusterStartupRule.getCache())
.getService(QueryConfigurationService.class).getMethodAuthorizer();
Set<String> expectedParameters =
new HashSet<>(Arrays.asList(REGEX_AUTHORIZER_PARAMETERS.split(SPLITTING_REGEX)));
assertThat(methodAuthorizer.getAllowedPatterns()).isEqualTo(expectedParameters);
}));
}
@Test
public void alterQueryServiceCommandWithUserSpecifiedMethodAuthorizerUpdatesMethodAuthorizer()
throws IOException {
verifyCurrentAuthorizerClass(DEFAULT_AUTHORIZER_CLASS);
Class<TestMethodAuthorizer> authorizerClass = TestMethodAuthorizer.class;
String authorizerName = authorizerClass.getName();
String classContent =
new String(Files.readAllBytes(Paths.get(getTestMethodAuthorizerFilePath())));
String jarFileName = "testJar.jar";
File jarFile = tempFolder.newFile(jarFileName);
new ClassBuilder().writeJarFromContent(authorizerName, classContent, jarFile);
gfsh.executeAndAssertThat("deploy --jars=" + jarFile.getAbsolutePath()).statusIsSuccess();
executeAndAssertGfshCommandIsSuccessful(TestMethodAuthorizer.class, USER_AUTHORIZER_PARAMETERS);
servers.forEach(server -> server.invoke(() -> {
TestMethodAuthorizer methodAuthorizer =
(TestMethodAuthorizer) Objects.requireNonNull(ClusterStartupRule.getCache())
.getService(QueryConfigurationService.class).getMethodAuthorizer();
Set<String> expectedParameters =
new HashSet<>(Arrays.asList(USER_AUTHORIZER_PARAMETERS.split(SPLITTING_REGEX)));
Assertions.assertThat(methodAuthorizer.getParameters()).isEqualTo(expectedParameters);
}));
}
@Test
public void alterQueryServiceCommandUpdatesClusterConfigWhenNoMembersAreFound() {
servers.forEach(VMProvider::stop);
Class<JavaBeanAccessorMethodAuthorizer> authorizerClass =
JavaBeanAccessorMethodAuthorizer.class;
String authorizerName = authorizerClass.getName();
gfsh.executeAndAssertThat(buildCommand(authorizerName, JAVA_BEAN_AUTHORIZER_PARAMETERS))
.statusIsSuccess().hasInfoSection().hasLines().contains(NO_MEMBERS_FOUND_MESSAGE);
startServerAndVerifyJavaBeanAccessorMethodAuthorizer();
}
@Test
public void alterQueryServiceCommandDoesNotUpdateClusterConfigWhenFunctionExecutionFailsOnAllMembers() {
String authorizerName = "badAuthorizerName";
gfsh.executeAndAssertThat(buildCommand(authorizerName, JAVA_BEAN_AUTHORIZER_PARAMETERS))
.statusIsError();
locator.invoke(() -> assertThat(Objects.requireNonNull(ClusterStartupRule.getLocator())
.getConfigurationPersistenceService().getCacheConfig(null)).isNull());
}
}