Make capacity/validity/updateinterval/activeupdate for Auth Caches configurable via nodetool

patch by Aleksei Zotov; reviewed by Josh McKenzie for CASSANDRA-17063
This commit is contained in:
Aleksei Zotov 2021-12-20 21:26:41 +04:00
parent 7bd2764962
commit bc20bddceb
14 changed files with 549 additions and 43 deletions

View File

@ -1,4 +1,5 @@
4.1
* Make capacity/validity/updateinterval/activeupdate for Auth Caches configurable via nodetool (CASSANDRA-17063)
* Added startup check for read_ahead_kb setting (CASSANDRA-16436)
* Avoid unecessary array allocations and initializations when performing query checks (CASSANDRA-17209)
* Add guardrail for list operations that require read before write (CASSANDRA-17154)

View File

@ -38,6 +38,7 @@ using the provided 'sstableupgrade' tool.
New features
------------
- Added ability to configure auth caches through corresponding `nodetool` commands.
- CDC data flushing now can be configured to be non-blocking with the configuration cdc_block_writes. Setting to true,
any writes to the CDC-enabled tables will be blocked when reaching to the limit for CDC data on disk, which is the
existing and the default behavior. Setting to false, the writes to the CDC-enabled tables will be accepted and

View File

@ -61,6 +61,7 @@ import org.apache.cassandra.audit.AuditLogOptions;
import org.apache.cassandra.audit.AuditLogOptionsCompositeData;
import com.google.common.collect.ImmutableMap;
import org.apache.cassandra.auth.AuthCache;
import org.apache.cassandra.auth.AuthCacheMBean;
import org.apache.cassandra.auth.NetworkPermissionsCache;
import org.apache.cassandra.auth.NetworkPermissionsCacheMBean;
import org.apache.cassandra.auth.PasswordAuthenticator;
@ -569,6 +570,25 @@ public class NodeProbe implements AutoCloseable
cacheService.invalidateRowCache();
}
public AuthCacheMBean getAuthCacheMBean(String cacheName)
{
switch (cacheName)
{
case PasswordAuthenticator.CredentialsCacheMBean.CACHE_NAME:
return ccProxy;
case AuthorizationProxy.JmxPermissionsCacheMBean.CACHE_NAME:
return jpcProxy;
case NetworkPermissionsCacheMBean.CACHE_NAME:
return npcProxy;
case PermissionsCacheMBean.CACHE_NAME:
return pcProxy;
case RolesCacheMBean.CACHE_NAME:
return rcProxy;
default:
throw new IllegalArgumentException("Unknown cache name: " + cacheName);
}
}
public void drain() throws IOException, InterruptedException, ExecutionException
{
ssProxy.drain();

View File

@ -129,6 +129,7 @@ public class NodeTool
GarbageCollect.class,
GcStats.class,
GetAuditLog.class,
GetAuthCacheConfig.class,
GetBatchlogReplayTrottle.class,
GetColumnIndexSize.class,
GetCompactionThreshold.class,
@ -187,6 +188,7 @@ public class NodeTool
ResumeHandoff.class,
Ring.class,
Scrub.class,
SetAuthCacheConfig.class,
SetBatchlogReplayThrottle.class,
SetCacheCapacity.class,
SetCacheKeysToSave.class,

View File

@ -0,0 +1,47 @@
/*
* 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.cassandra.tools.nodetool;
import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.cassandra.auth.AuthCacheMBean;
import org.apache.cassandra.tools.NodeProbe;
import org.apache.cassandra.tools.NodeTool;
@Command(name = "getauthcacheconfig", description = "Get configuration of Auth cache")
public class GetAuthCacheConfig extends NodeTool.NodeToolCmd
{
@SuppressWarnings("unused")
@Option(title = "cache-name",
name = {"--cache-name"},
description = "Name of Auth cache (required)",
required = true)
private String cacheName;
@Override
public void execute(NodeProbe probe)
{
AuthCacheMBean authCacheMBean = probe.getAuthCacheMBean(cacheName);
probe.output().out.println("Validity Period: " + authCacheMBean.getValidity());
probe.output().out.println("Update Interval: " + authCacheMBean.getUpdateInterval());
probe.output().out.println("Max Entries: " + authCacheMBean.getMaxEntries());
probe.output().out.println("Active Update: " + authCacheMBean.getActiveUpdate());
}
}

View File

@ -182,7 +182,7 @@ public class InvalidatePermissionsCache extends NodeToolCmd
return FunctionResource.fromName("functions/" + functionsInKeyspace + '/' + function).getName();
} catch (ConfigurationException e)
{
throw new IllegalArgumentException("An error was encountered when looking up function definition; " + e.getMessage());
throw new IllegalArgumentException("An error was encountered when looking up function definition: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.cassandra.tools.nodetool;
import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.cassandra.auth.AuthCacheMBean;
import org.apache.cassandra.tools.NodeProbe;
import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
import static com.google.common.base.Preconditions.checkArgument;
@Command(name = "setauthcacheconfig", description = "Set configuration for Auth cache")
public class SetAuthCacheConfig extends NodeToolCmd
{
@SuppressWarnings("unused")
@Option(title = "cache-name",
name = {"--cache-name"},
description = "Name of Auth cache (required)",
required = true)
private String cacheName;
@SuppressWarnings("unused")
@Option(title = "validity-period",
name = {"--validity-period"},
description = "Validity period in milliseconds")
private Integer validityPeriod;
@SuppressWarnings("unused")
@Option(title = "update-interval",
name = {"--update-interval"},
description = "Update interval in milliseconds")
private Integer updateInterval;
@SuppressWarnings("unused")
@Option(title = "max-entries",
name = {"--max-entries"},
description = "Max entries")
private Integer maxEntries;
@SuppressWarnings("unused")
@Option(title = "enable-active-update",
name = {"--enable-active-update"},
description = "Enable active update")
private Boolean enableActiveUpdate;
@SuppressWarnings("unused")
@Option(title = "disable-active-update",
name = {"--disable-active-update"},
description = "Disable active update")
private Boolean disableActiveUpdate;
@Override
public void execute(NodeProbe probe)
{
Boolean activeUpdate = getActiveUpdate(enableActiveUpdate, disableActiveUpdate);
checkArgument(validityPeriod != null || updateInterval != null
|| maxEntries != null || activeUpdate != null,
"At least one optional parameter need to be passed");
AuthCacheMBean authCacheMBean = probe.getAuthCacheMBean(cacheName);
if (validityPeriod != null)
{
authCacheMBean.setValidity(validityPeriod);
probe.output().out.println("Changed Validity Period to " + validityPeriod);
}
if (updateInterval != null)
{
authCacheMBean.setUpdateInterval(updateInterval);
probe.output().out.println("Changed Update Interval to " + updateInterval);
}
if (maxEntries != null)
{
authCacheMBean.setMaxEntries(maxEntries);
probe.output().out.println("Changed Max Entries to " + maxEntries);
}
if (activeUpdate != null)
{
authCacheMBean.setActiveUpdate(activeUpdate);
probe.output().out.println("Changed Active Update to " + activeUpdate);
}
}
private Boolean getActiveUpdate(Boolean enableActiveUpdate, Boolean disableActiveUpdate)
{
if (enableActiveUpdate == null && disableActiveUpdate == null)
return null;
if (enableActiveUpdate != null && disableActiveUpdate != null)
throw new IllegalArgumentException("enable-active-update and disable-active-update cannot be used together");
return Boolean.TRUE.equals(enableActiveUpdate) ? Boolean.TRUE : Boolean.FALSE;
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.cassandra.tools.nodetool;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.auth.AuthCache;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.NetworkPermissionsCacheMBean;
import org.apache.cassandra.auth.PasswordAuthenticator;
import org.apache.cassandra.auth.PermissionsCacheMBean;
import org.apache.cassandra.auth.Roles;
import org.apache.cassandra.auth.RolesCacheMBean;
import org.apache.cassandra.auth.jmx.AuthorizationProxy;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
import static org.assertj.core.api.Assertions.assertThat;
public class GetAuthCacheConfigTest extends CQLTester
{
@BeforeClass
public static void setup() throws Exception
{
CQLTester.setUpClass();
CQLTester.requireAuthentication();
startJMXServer();
}
@Test
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testMaybeChangeDocs()
{
// If you added, modified options or help, please update docs if necessary
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "getauthcacheconfig");
tool.assertOnCleanExit();
String help = "NAME\n" +
" nodetool getauthcacheconfig - Get configuration of Auth cache\n" +
"\n" +
"SYNOPSIS\n" +
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
" [(-u <username> | --username <username>)] getauthcacheconfig\n" +
" --cache-name <cache-name>\n" +
"\n" +
"OPTIONS\n" +
" --cache-name <cache-name>\n" +
" Name of Auth cache (required)\n" +
"\n" +
" -h <host>, --host <host>\n" +
" Node hostname or ip address\n" +
"\n" +
" -p <port>, --port <port>\n" +
" Remote jmx agent port number\n" +
"\n" +
" -pp, --print-port\n" +
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
"\n" +
" -pw <password>, --password <password>\n" +
" Remote jmx agent password\n" +
"\n" +
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
" Path to the JMX password file\n" +
"\n" +
" -u <username>, --username <username>\n" +
" Remote jmx agent username\n" +
"\n" +
"\n";
assertThat(tool.getStdout()).isEqualTo(help);
}
@Test
public void testInvalidCacheName()
{
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("getauthcacheconfig");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required option '--cache-name' is missing"));
assertThat(tool.getStderr()).isEmpty();
tool = ToolRunner.invokeNodetool("getauthcacheconfig", "--cache-name");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required values for option 'cache-name' not provided"));
assertThat(tool.getStderr()).isEmpty();
tool = ToolRunner.invokeNodetool("getauthcacheconfig", "--cache-name", "wrong");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Unknown cache name: wrong"));
assertThat(tool.getStderr()).isEmpty();
}
@Test
public void testGetConfig()
{
assertGetConfig(AuthenticatedUser.permissionsCache, PermissionsCacheMBean.CACHE_NAME);
PasswordAuthenticator passwordAuthenticator = (PasswordAuthenticator) DatabaseDescriptor.getAuthenticator();
assertGetConfig(passwordAuthenticator.getCredentialsCache(), PasswordAuthenticator.CredentialsCacheMBean.CACHE_NAME);
assertGetConfig(AuthorizationProxy.jmxPermissionsCache, AuthorizationProxy.JmxPermissionsCacheMBean.CACHE_NAME);
assertGetConfig(AuthenticatedUser.networkPermissionsCache, NetworkPermissionsCacheMBean.CACHE_NAME);
assertGetConfig(Roles.cache, RolesCacheMBean.CACHE_NAME);
}
@SuppressWarnings("SingleCharacterStringConcatenation")
private void assertGetConfig(AuthCache<?, ?> authCache, String cacheName)
{
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("getauthcacheconfig", "--cache-name", cacheName);
tool.assertOnCleanExit();
assertThat(tool.getStdout()).isEqualTo("Validity Period: " + authCache.getValidity() + "\n" +
"Update Interval: " + authCache.getUpdateInterval() + "\n" +
"Max Entries: " + authCache.getMaxEntries() + "\n" +
"Active Update: " + authCache.getActiveUpdate() + "\n");
}
private String wrapByDefaultNodetoolMessage(String s)
{
return "nodetool: " + s + "\nSee 'nodetool help' or 'nodetool help <command>'.\n";
}
}

View File

@ -23,11 +23,12 @@ import org.junit.Test;
import com.datastax.driver.core.EndPoint;
import com.datastax.driver.core.PlainTextAuthProvider;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.auth.AuthTestUtils;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.IAuthenticator;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.auth.PasswordAuthenticator;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
@ -44,17 +45,14 @@ public class InvalidateCredentialsCacheTest extends CQLTester
@BeforeClass
public static void setup() throws Exception
{
SchemaLoader.prepareServer();
AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager();
PasswordAuthenticator passwordAuthenticator = new AuthTestUtils.LocalPasswordAuthenticator();
SchemaLoader.setupAuth(roleManager,
passwordAuthenticator,
new AuthTestUtils.LocalCassandraAuthorizer(),
new AuthTestUtils.LocalCassandraNetworkAuthorizer());
CQLTester.setUpClass();
CQLTester.requireAuthentication();
IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());
PasswordAuthenticator passwordAuthenticator = (PasswordAuthenticator) DatabaseDescriptor.getAuthenticator();
roleANegotiator = passwordAuthenticator.newSaslNegotiator(null);
roleANegotiator.evaluateResponse(new PlainTextAuthProvider(ROLE_A.getRoleName(), "ignored")
.newAuthenticator((EndPoint) null, null)

View File

@ -24,13 +24,15 @@ import javax.security.auth.Subject;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.auth.AuthTestUtils;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.CassandraPrincipal;
import org.apache.cassandra.auth.IAuthorizer;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.auth.JMXResource;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.auth.jmx.AuthorizationProxy;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
@ -46,21 +48,18 @@ public class InvalidateJmxPermissionsCacheTest extends CQLTester
@BeforeClass
public static void setup() throws Exception
{
SchemaLoader.prepareServer();
AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager();
AuthTestUtils.LocalCassandraAuthorizer authorizer = new AuthTestUtils.LocalCassandraAuthorizer();
SchemaLoader.setupAuth(roleManager,
new AuthTestUtils.LocalPasswordAuthenticator(),
authorizer,
new AuthTestUtils.LocalCassandraNetworkAuthorizer());
CQLTester.setUpClass();
CQLTester.requireAuthentication();
IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());
JMXResource rootJmxResource = JMXResource.root();
Set<Permission> jmxPermissions = rootJmxResource.applicablePermissions();
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
IAuthorizer authorizer = DatabaseDescriptor.getAuthorizer();
authorizer.grant(AuthenticatedUser.SYSTEM_USER, jmxPermissions, rootJmxResource, ROLE_A);
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());
authorizer.grant(AuthenticatedUser.SYSTEM_USER, jmxPermissions, rootJmxResource, ROLE_B);
startJMXServer();

View File

@ -21,9 +21,10 @@ package org.apache.cassandra.tools.nodetool;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.auth.AuthTestUtils;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
@ -37,13 +38,10 @@ public class InvalidateNetworkPermissionsCacheTest extends CQLTester
@BeforeClass
public static void setup() throws Exception
{
SchemaLoader.prepareServer();
AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager();
SchemaLoader.setupAuth(roleManager,
new AuthTestUtils.LocalPasswordAuthenticator(),
new AuthTestUtils.LocalCassandraAuthorizer(),
new AuthTestUtils.LocalCassandraNetworkAuthorizer());
CQLTester.setUpClass();
CQLTester.requireAuthentication();
IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());

View File

@ -28,12 +28,13 @@ import org.apache.commons.lang3.StringUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.auth.AuthTestUtils;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.DataResource;
import org.apache.cassandra.auth.FunctionResource;
import org.apache.cassandra.auth.IAuthorizer;
import org.apache.cassandra.auth.IResource;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.auth.JMXResource;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.auth.RoleResource;
@ -52,14 +53,10 @@ public class InvalidatePermissionsCacheTest extends CQLTester
@BeforeClass
public static void setup() throws Exception
{
SchemaLoader.prepareServer();
AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager();
AuthTestUtils.LocalCassandraAuthorizer authorizer = new AuthTestUtils.LocalCassandraAuthorizer();
SchemaLoader.setupAuth(roleManager,
new AuthTestUtils.LocalPasswordAuthenticator(),
authorizer,
new AuthTestUtils.LocalCassandraNetworkAuthorizer());
CQLTester.setUpClass();
CQLTester.requireAuthentication();
IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());
@ -78,6 +75,7 @@ public class InvalidatePermissionsCacheTest extends CQLTester
JMXResource.root(),
JMXResource.mbean("org.apache.cassandra.auth:type=*"));
IAuthorizer authorizer = DatabaseDescriptor.getAuthorizer();
for (IResource resource : resources)
{
Set<Permission> permissions = resource.applicablePermissions();
@ -221,7 +219,7 @@ public class InvalidatePermissionsCacheTest extends CQLTester
KEYSPACE, "--function", "f[x]");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout())
.isEqualTo(wrapByDefaultNodetoolMessage("An error was encountered when looking up function definition; Unable to find abstract-type class 'org.apache.cassandra.db.marshal.x'"));
.isEqualTo(wrapByDefaultNodetoolMessage("An error was encountered when looking up function definition: Unable to find abstract-type class 'org.apache.cassandra.db.marshal.x'"));
assertThat(tool.getStderr()).isEmpty();
}

View File

@ -21,9 +21,10 @@ package org.apache.cassandra.tools.nodetool;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.auth.AuthTestUtils;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
@ -37,13 +38,10 @@ public class InvalidateRolesCacheTest extends CQLTester
@BeforeClass
public static void setup() throws Exception
{
SchemaLoader.prepareServer();
AuthTestUtils.LocalCassandraRoleManager roleManager = new AuthTestUtils.LocalCassandraRoleManager();
SchemaLoader.setupAuth(roleManager,
new AuthTestUtils.LocalPasswordAuthenticator(),
new AuthTestUtils.LocalCassandraAuthorizer(),
new AuthTestUtils.LocalCassandraNetworkAuthorizer());
CQLTester.setUpClass();
CQLTester.requireAuthentication();
IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, AuthTestUtils.getLoginRoleOptions());
roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, AuthTestUtils.getLoginRoleOptions());

View File

@ -0,0 +1,189 @@
/*
* 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.cassandra.tools.nodetool;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.auth.AuthCache;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.NetworkPermissionsCacheMBean;
import org.apache.cassandra.auth.PasswordAuthenticator;
import org.apache.cassandra.auth.PermissionsCacheMBean;
import org.apache.cassandra.auth.Roles;
import org.apache.cassandra.auth.RolesCacheMBean;
import org.apache.cassandra.auth.jmx.AuthorizationProxy;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.tools.ToolRunner;
import static org.assertj.core.api.Assertions.assertThat;
public class SetAuthCacheConfigTest extends CQLTester
{
@BeforeClass
public static void setup() throws Exception
{
CQLTester.setUpClass();
CQLTester.requireAuthentication();
startJMXServer();
}
@Test
@SuppressWarnings("SingleCharacterStringConcatenation")
public void testMaybeChangeDocs()
{
// If you added, modified options or help, please update docs if necessary
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "setauthcacheconfig");
tool.assertOnCleanExit();
String help = "NAME\n" +
" nodetool setauthcacheconfig - Set configuration for Auth cache\n" +
"\n" +
"SYNOPSIS\n" +
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
" [(-u <username> | --username <username>)] setauthcacheconfig\n" +
" --cache-name <cache-name> [--disable-active-update]\n" +
" [--enable-active-update] [--max-entries <max-entries>]\n" +
" [--update-interval <update-interval>]\n" +
" [--validity-period <validity-period>]\n" +
"\n" +
"OPTIONS\n" +
" --cache-name <cache-name>\n" +
" Name of Auth cache (required)\n" +
"\n" +
" --disable-active-update\n" +
" Disable active update\n" +
"\n" +
" --enable-active-update\n" +
" Enable active update\n" +
"\n" +
" -h <host>, --host <host>\n" +
" Node hostname or ip address\n" +
"\n" +
" --max-entries <max-entries>\n" +
" Max entries\n" +
"\n" +
" -p <port>, --port <port>\n" +
" Remote jmx agent port number\n" +
"\n" +
" -pp, --print-port\n" +
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
"\n" +
" -pw <password>, --password <password>\n" +
" Remote jmx agent password\n" +
"\n" +
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
" Path to the JMX password file\n" +
"\n" +
" -u <username>, --username <username>\n" +
" Remote jmx agent username\n" +
"\n" +
" --update-interval <update-interval>\n" +
" Update interval in milliseconds\n" +
"\n" +
" --validity-period <validity-period>\n" +
" Validity period in milliseconds\n" +
"\n" +
"\n";
assertThat(tool.getStdout()).isEqualTo(help);
}
@Test
public void testInvalidCacheName()
{
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("setauthcacheconfig");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required option '--cache-name' is missing"));
assertThat(tool.getStderr()).isEmpty();
tool = ToolRunner.invokeNodetool("setauthcacheconfig", "--cache-name");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Required values for option 'cache-name' not provided"));
assertThat(tool.getStderr()).isEmpty();
tool = ToolRunner.invokeNodetool("setauthcacheconfig", "--cache-name", "wrong", "--validity-period", "1");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("Unknown cache name: wrong"));
assertThat(tool.getStderr()).isEmpty();
}
@Test
public void testNoOptionalParameters()
{
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("setauthcacheconfig", "--cache-name", "PermissionCache");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("At least one optional parameter need to be passed"));
assertThat(tool.getStderr()).isEmpty();
}
@Test
public void testBothEnableAndDisableActiveUpdate()
{
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("setauthcacheconfig",
"--cache-name", "PermissionCache",
"--enable-active-update",
"--disable-active-update");
assertThat(tool.getExitCode()).isEqualTo(1);
assertThat(tool.getStdout()).isEqualTo(wrapByDefaultNodetoolMessage("enable-active-update and disable-active-update cannot be used together"));
assertThat(tool.getStderr()).isEmpty();
}
@Test
public void testSetConfig()
{
assertSetConfig(AuthenticatedUser.permissionsCache, PermissionsCacheMBean.CACHE_NAME);
PasswordAuthenticator passwordAuthenticator = (PasswordAuthenticator) DatabaseDescriptor.getAuthenticator();
assertSetConfig(passwordAuthenticator.getCredentialsCache(), PasswordAuthenticator.CredentialsCacheMBean.CACHE_NAME);
assertSetConfig(AuthorizationProxy.jmxPermissionsCache, AuthorizationProxy.JmxPermissionsCacheMBean.CACHE_NAME);
assertSetConfig(AuthenticatedUser.networkPermissionsCache, NetworkPermissionsCacheMBean.CACHE_NAME);
assertSetConfig(Roles.cache, RolesCacheMBean.CACHE_NAME);
}
private void assertSetConfig(AuthCache<?, ?> authCache, String cacheName)
{
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("setauthcacheconfig",
"--cache-name", cacheName,
"--validity-period", "1",
"--update-interval", "2",
"--max-entries", "3",
"--disable-active-update");
tool.assertOnCleanExit();
assertThat(tool.getStdout()).isEqualTo("Changed Validity Period to 1\n" +
"Changed Update Interval to 2\n" +
"Changed Max Entries to 3\n" +
"Changed Active Update to false\n");
assertThat(authCache.getValidity()).isEqualTo(1);
assertThat(authCache.getUpdateInterval()).isEqualTo(2);
assertThat(authCache.getMaxEntries()).isEqualTo(3);
assertThat(authCache.getActiveUpdate()).isFalse();
}
private String wrapByDefaultNodetoolMessage(String s)
{
return "nodetool: " + s + "\nSee 'nodetool help' or 'nodetool help <command>'.\n";
}
}