This is an automated email from the ASF dual-hosted git repository.
samt pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new f9aa19e Add nodetool commands to invalidate auth caches
f9aa19e is described below
commit f9aa19e3b116c0078019e9382d1a6c4bb050f113
Author: Aleksei Zotov <[email protected]>
AuthorDate: Thu Aug 12 14:52:31 2021 +0100
Add nodetool commands to invalidate auth caches
Patch by Aleksei Zotov; reviewed by Benjamin Lerer, Sumanth Pasupuleti
and Sam Tunnicliffe for CASSANDRA-16404
---
CHANGES.txt | 1 +
src/java/org/apache/cassandra/auth/AuthCache.java | 2 +-
.../apache/cassandra/auth/AuthenticatedUser.java | 4 +-
.../apache/cassandra/auth/INetworkAuthorizer.java | 2 +-
...AuthCache.java => NetworkPermissionsCache.java} | 21 +-
...ache.java => NetworkPermissionsCacheMBean.java} | 21 +-
.../cassandra/auth/PasswordAuthenticator.java | 20 +-
.../apache/cassandra/auth/PermissionsCache.java | 8 +-
...rkAuthCache.java => PermissionsCacheMBean.java} | 19 +-
src/java/org/apache/cassandra/auth/RolesCache.java | 9 +-
...{NetworkAuthCache.java => RolesCacheMBean.java} | 19 +-
.../cassandra/auth/jmx/AuthorizationProxy.java | 35 ++-
src/java/org/apache/cassandra/tools/NodeProbe.java | 75 ++++-
src/java/org/apache/cassandra/tools/NodeTool.java | 31 +-
.../tools/nodetool/InvalidateCredentialsCache.java | 49 ++++
.../nodetool/InvalidateJmxPermissionsCache.java | 48 ++++
.../InvalidateNetworkPermissionsCache.java | 49 ++++
.../tools/nodetool/InvalidatePermissionsCache.java | 174 +++++++++++
.../tools/nodetool/InvalidateRolesCache.java | 50 ++++
.../{RoleTestUtils.java => AuthTestUtils.java} | 69 ++++-
.../auth/CassandraNetworkAuthorizerTest.java | 50 +---
.../cassandra/auth/CassandraRoleManagerTest.java | 6 +-
test/unit/org/apache/cassandra/auth/RolesTest.java | 18 +-
.../nodetool/InvalidateCredentialsCacheTest.java | 171 +++++++++++
.../InvalidateJmxPermissionsCacheTest.java | 192 +++++++++++++
.../InvalidateNetworkPermissionsCacheTest.java | 160 +++++++++++
.../nodetool/InvalidatePermissionsCacheTest.java | 317 +++++++++++++++++++++
.../tools/nodetool/InvalidateRolesCacheTest.java | 159 +++++++++++
28 files changed, 1630 insertions(+), 149 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 8555e51..a2a4043 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
4.1
+ * Provide a nodetool command to invalidate auth caches (CASSANDRA-16404)
* Catch read repair timeout exceptions and add metric (CASSANDRA-16880)
* Exclude Jackson 1.x transitive dependency of hadoop* provided dependencies
(CASSANDRA-16854)
* Add client warnings and abort to tombstone and coordinator reads which go
past a low/high watermark (CASSANDRA-16850)
diff --git a/src/java/org/apache/cassandra/auth/AuthCache.java
b/src/java/org/apache/cassandra/auth/AuthCache.java
index 6393da7..32e9f0f 100644
--- a/src/java/org/apache/cassandra/auth/AuthCache.java
+++ b/src/java/org/apache/cassandra/auth/AuthCache.java
@@ -38,7 +38,7 @@ public class AuthCache<K, V> implements AuthCacheMBean
{
private static final Logger logger =
LoggerFactory.getLogger(AuthCache.class);
- private static final String MBEAN_NAME_BASE =
"org.apache.cassandra.auth:type=";
+ public static final String MBEAN_NAME_BASE =
"org.apache.cassandra.auth:type=";
/**
* Underlying cache. LoadingCache will call underlying load function on
{@link #get} if key is not present
diff --git a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
index 9f22bea..c2d93ca 100644
--- a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
+++ b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
@@ -40,7 +40,7 @@ public class AuthenticatedUser
// User-level permissions cache.
private static final PermissionsCache permissionsCache = new
PermissionsCache(DatabaseDescriptor.getAuthorizer());
- private static final NetworkAuthCache networkAuthCache = new
NetworkAuthCache(DatabaseDescriptor.getNetworkAuthorizer());
+ private static final NetworkPermissionsCache networkPermissionsCache = new
NetworkPermissionsCache(DatabaseDescriptor.getNetworkAuthorizer());
private final String name;
// primary Role of the logged in user
@@ -136,7 +136,7 @@ public class AuthenticatedUser
*/
public boolean hasLocalAccess()
{
- return
networkAuthCache.get(this.getPrimaryRole()).canAccess(Datacenters.thisDatacenter());
+ return
networkPermissionsCache.get(this.getPrimaryRole()).canAccess(Datacenters.thisDatacenter());
}
@Override
diff --git a/src/java/org/apache/cassandra/auth/INetworkAuthorizer.java
b/src/java/org/apache/cassandra/auth/INetworkAuthorizer.java
index 4582b5e..9a5a5d6 100644
--- a/src/java/org/apache/cassandra/auth/INetworkAuthorizer.java
+++ b/src/java/org/apache/cassandra/auth/INetworkAuthorizer.java
@@ -46,7 +46,7 @@ public interface INetworkAuthorizer
void setRoleDatacenters(RoleResource role, DCPermissions permissions);
/**
- * Called when a role is deleted, so any corresponding network auth
+ * Called when a role is deleted, so any corresponding network permissions
* data can also be cleaned up
*/
void drop(RoleResource role);
diff --git a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
b/src/java/org/apache/cassandra/auth/NetworkPermissionsCache.java
similarity index 66%
copy from src/java/org/apache/cassandra/auth/NetworkAuthCache.java
copy to src/java/org/apache/cassandra/auth/NetworkPermissionsCache.java
index 6b3c74e..72817a9 100644
--- a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
+++ b/src/java/org/apache/cassandra/auth/NetworkPermissionsCache.java
@@ -19,12 +19,13 @@
package org.apache.cassandra.auth;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.utils.MBeanWrapper;
-public class NetworkAuthCache extends AuthCache<RoleResource, DCPermissions>
+public class NetworkPermissionsCache extends AuthCache<RoleResource,
DCPermissions> implements NetworkPermissionsCacheMBean
{
- public NetworkAuthCache(INetworkAuthorizer authorizer)
+ public NetworkPermissionsCache(INetworkAuthorizer authorizer)
{
- super("NetworkAuthCache",
+ super(CACHE_NAME,
DatabaseDescriptor::setRolesValidity,
DatabaseDescriptor::getRolesValidity,
DatabaseDescriptor::setRolesUpdateInterval,
@@ -33,5 +34,19 @@ public class NetworkAuthCache extends
AuthCache<RoleResource, DCPermissions>
DatabaseDescriptor::getRolesCacheMaxEntries,
authorizer::authorize,
() ->
DatabaseDescriptor.getAuthenticator().requireAuthentication());
+
+ MBeanWrapper.instance.registerMBean(this, MBEAN_NAME_BASE +
DEPRECATED_CACHE_NAME);
+ }
+
+ public void invalidateNetworkPermissions(String roleName)
+ {
+ invalidate(RoleResource.role(roleName));
+ }
+
+ @Override
+ protected void unregisterMBean()
+ {
+ super.unregisterMBean();
+ MBeanWrapper.instance.unregisterMBean(MBEAN_NAME_BASE +
DEPRECATED_CACHE_NAME, MBeanWrapper.OnException.LOG);
}
}
diff --git a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
b/src/java/org/apache/cassandra/auth/NetworkPermissionsCacheMBean.java
similarity index 54%
copy from src/java/org/apache/cassandra/auth/NetworkAuthCache.java
copy to src/java/org/apache/cassandra/auth/NetworkPermissionsCacheMBean.java
index 6b3c74e..b0e72b0 100644
--- a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
+++ b/src/java/org/apache/cassandra/auth/NetworkPermissionsCacheMBean.java
@@ -18,20 +18,11 @@
package org.apache.cassandra.auth;
-import org.apache.cassandra.config.DatabaseDescriptor;
-
-public class NetworkAuthCache extends AuthCache<RoleResource, DCPermissions>
+public interface NetworkPermissionsCacheMBean extends AuthCacheMBean
{
- public NetworkAuthCache(INetworkAuthorizer authorizer)
- {
- super("NetworkAuthCache",
- DatabaseDescriptor::setRolesValidity,
- DatabaseDescriptor::getRolesValidity,
- DatabaseDescriptor::setRolesUpdateInterval,
- DatabaseDescriptor::getRolesUpdateInterval,
- DatabaseDescriptor::setRolesCacheMaxEntries,
- DatabaseDescriptor::getRolesCacheMaxEntries,
- authorizer::authorize,
- () ->
DatabaseDescriptor.getAuthenticator().requireAuthentication());
- }
+ public static final String CACHE_NAME = "NetworkPermissionsCache";
+ @Deprecated
+ public static final String DEPRECATED_CACHE_NAME = "NetworkAuthCache";
+
+ public void invalidateNetworkPermissions(String roleName);
}
diff --git a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index 9da99a9..00ccab7 100644
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.Map;
import java.util.Set;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
@@ -103,11 +104,10 @@ public class PasswordAuthenticator implements
IAuthenticator
{
try
{
- ResultMessage.Rows rows =
- authenticateStatement.execute(QueryState.forInternalCalls(),
-
QueryOptions.forInternalCalls(consistencyForRole(username),
-
Lists.newArrayList(ByteBufferUtil.bytes(username))),
- System.nanoTime());
+ QueryOptions options =
QueryOptions.forInternalCalls(consistencyForRole(username),
+ Lists.newArrayList(ByteBufferUtil.bytes(username)));
+
+ ResultMessage.Rows rows = select(authenticateStatement, options);
// If either a non-existent role name was supplied, or no
credentials
// were found for that role we don't want to cache the result so
we throw
@@ -127,6 +127,12 @@ public class PasswordAuthenticator implements
IAuthenticator
}
}
+ @VisibleForTesting
+ ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
+ {
+ return statement.execute(QueryState.forInternalCalls(), options,
System.nanoTime());
+ }
+
public Set<DataResource> protectedResources()
{
// Also protected by CassandraRoleManager, but the duplication doesn't
hurt and is more explicit
@@ -243,7 +249,7 @@ public class PasswordAuthenticator implements IAuthenticator
{
private CredentialsCache(PasswordAuthenticator authenticator)
{
- super("CredentialsCache",
+ super(CACHE_NAME,
DatabaseDescriptor::setCredentialsValidity,
DatabaseDescriptor::getCredentialsValidity,
DatabaseDescriptor::setCredentialsUpdateInterval,
@@ -262,6 +268,8 @@ public class PasswordAuthenticator implements IAuthenticator
public static interface CredentialsCacheMBean extends AuthCacheMBean
{
+ public static final String CACHE_NAME = "CredentialsCache";
+
public void invalidateCredentials(String roleName);
}
}
diff --git a/src/java/org/apache/cassandra/auth/PermissionsCache.java
b/src/java/org/apache/cassandra/auth/PermissionsCache.java
index a33f5d1..a649c35 100644
--- a/src/java/org/apache/cassandra/auth/PermissionsCache.java
+++ b/src/java/org/apache/cassandra/auth/PermissionsCache.java
@@ -23,10 +23,11 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.utils.Pair;
public class PermissionsCache extends AuthCache<Pair<AuthenticatedUser,
IResource>, Set<Permission>>
+ implements PermissionsCacheMBean
{
public PermissionsCache(IAuthorizer authorizer)
{
- super("PermissionsCache",
+ super(CACHE_NAME,
DatabaseDescriptor::setPermissionsValidity,
DatabaseDescriptor::getPermissionsValidity,
DatabaseDescriptor::setPermissionsUpdateInterval,
@@ -41,4 +42,9 @@ public class PermissionsCache extends
AuthCache<Pair<AuthenticatedUser, IResourc
{
return get(Pair.create(user, resource));
}
+
+ public void invalidatePermissions(String userName, String resourceName)
+ {
+ invalidate(Pair.create(new AuthenticatedUser(userName),
Resources.fromName(resourceName)));
+ }
}
diff --git a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
b/src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java
similarity index 54%
copy from src/java/org/apache/cassandra/auth/NetworkAuthCache.java
copy to src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java
index 6b3c74e..f2116d1 100644
--- a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
+++ b/src/java/org/apache/cassandra/auth/PermissionsCacheMBean.java
@@ -18,20 +18,9 @@
package org.apache.cassandra.auth;
-import org.apache.cassandra.config.DatabaseDescriptor;
-
-public class NetworkAuthCache extends AuthCache<RoleResource, DCPermissions>
+public interface PermissionsCacheMBean extends AuthCacheMBean
{
- public NetworkAuthCache(INetworkAuthorizer authorizer)
- {
- super("NetworkAuthCache",
- DatabaseDescriptor::setRolesValidity,
- DatabaseDescriptor::getRolesValidity,
- DatabaseDescriptor::setRolesUpdateInterval,
- DatabaseDescriptor::getRolesUpdateInterval,
- DatabaseDescriptor::setRolesCacheMaxEntries,
- DatabaseDescriptor::getRolesCacheMaxEntries,
- authorizer::authorize,
- () ->
DatabaseDescriptor.getAuthenticator().requireAuthentication());
- }
+ public static final String CACHE_NAME = "PermissionsCache";
+
+ public void invalidatePermissions(String userName, String resourceName);
}
diff --git a/src/java/org/apache/cassandra/auth/RolesCache.java
b/src/java/org/apache/cassandra/auth/RolesCache.java
index d01de63..62fecfb 100644
--- a/src/java/org/apache/cassandra/auth/RolesCache.java
+++ b/src/java/org/apache/cassandra/auth/RolesCache.java
@@ -23,11 +23,11 @@ import java.util.stream.Collectors;
import org.apache.cassandra.config.DatabaseDescriptor;
-public class RolesCache extends AuthCache<RoleResource, Set<Role>>
+public class RolesCache extends AuthCache<RoleResource, Set<Role>> implements
RolesCacheMBean
{
public RolesCache(IRoleManager roleManager, BooleanSupplier enableCache)
{
- super("RolesCache",
+ super(CACHE_NAME,
DatabaseDescriptor::setRolesValidity,
DatabaseDescriptor::getRolesValidity,
DatabaseDescriptor::setRolesUpdateInterval,
@@ -62,4 +62,9 @@ public class RolesCache extends AuthCache<RoleResource,
Set<Role>>
{
return get(primaryRole);
}
+
+ public void invalidateRoles(String roleName)
+ {
+ invalidate(RoleResource.role(roleName));
+ }
}
diff --git a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
b/src/java/org/apache/cassandra/auth/RolesCacheMBean.java
similarity index 54%
rename from src/java/org/apache/cassandra/auth/NetworkAuthCache.java
rename to src/java/org/apache/cassandra/auth/RolesCacheMBean.java
index 6b3c74e..18b3c40 100644
--- a/src/java/org/apache/cassandra/auth/NetworkAuthCache.java
+++ b/src/java/org/apache/cassandra/auth/RolesCacheMBean.java
@@ -18,20 +18,9 @@
package org.apache.cassandra.auth;
-import org.apache.cassandra.config.DatabaseDescriptor;
-
-public class NetworkAuthCache extends AuthCache<RoleResource, DCPermissions>
+public interface RolesCacheMBean extends AuthCacheMBean
{
- public NetworkAuthCache(INetworkAuthorizer authorizer)
- {
- super("NetworkAuthCache",
- DatabaseDescriptor::setRolesValidity,
- DatabaseDescriptor::getRolesValidity,
- DatabaseDescriptor::setRolesUpdateInterval,
- DatabaseDescriptor::getRolesUpdateInterval,
- DatabaseDescriptor::setRolesCacheMaxEntries,
- DatabaseDescriptor::getRolesCacheMaxEntries,
- authorizer::authorize,
- () ->
DatabaseDescriptor.getAuthenticator().requireAuthentication());
- }
+ public static final String CACHE_NAME = "RolesCache";
+
+ void invalidateRoles(String roleName);
}
diff --git a/src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
b/src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
index 68cff0c..9179062 100644
--- a/src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
+++ b/src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.auth.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.utils.MBeanWrapper;
/**
* Provides a proxy interface to the platform's MBeanServer instance to perform
@@ -103,7 +104,7 @@ public class AuthorizationProxy implements InvocationHandler
"registerMBean",
"unregisterMBean");
- private static final JMXPermissionsCache permissionsCache = new
JMXPermissionsCache();
+ private static final JmxPermissionsCache permissionsCache = new
JmxPermissionsCache();
private MBeanServer mbs;
/*
@@ -182,7 +183,7 @@ public class AuthorizationProxy implements InvocationHandler
* as an invocation of a method on the MBeanServer.
*/
@VisibleForTesting
- boolean authorize(Subject subject, String methodName, Object[] args)
+ public boolean authorize(Subject subject, String methodName, Object[] args)
{
logger.trace("Authorizing JMX method invocation {} for {}",
methodName,
@@ -476,11 +477,12 @@ public class AuthorizationProxy implements
InvocationHandler
.collect(Collectors.toSet());
}
- private static final class JMXPermissionsCache extends
AuthCache<RoleResource, Set<PermissionDetails>>
+ private static final class JmxPermissionsCache extends
AuthCache<RoleResource, Set<PermissionDetails>>
+ implements JmxPermissionsCacheMBean
{
- protected JMXPermissionsCache()
+ protected JmxPermissionsCache()
{
- super("JMXPermissionsCache",
+ super(CACHE_NAME,
DatabaseDescriptor::setPermissionsValidity,
DatabaseDescriptor::getPermissionsValidity,
DatabaseDescriptor::setPermissionsUpdateInterval,
@@ -489,6 +491,29 @@ public class AuthorizationProxy implements
InvocationHandler
DatabaseDescriptor::getPermissionsCacheMaxEntries,
AuthorizationProxy::loadPermissions,
() -> true);
+
+ MBeanWrapper.instance.registerMBean(this, MBEAN_NAME_BASE +
DEPRECATED_CACHE_NAME);
+ }
+
+ public void invalidatePermissions(String roleName)
+ {
+ invalidate(RoleResource.role(roleName));
}
+
+ @Override
+ protected void unregisterMBean()
+ {
+ super.unregisterMBean();
+ MBeanWrapper.instance.unregisterMBean(MBEAN_NAME_BASE +
DEPRECATED_CACHE_NAME, MBeanWrapper.OnException.LOG);
+ }
+ }
+
+ public static interface JmxPermissionsCacheMBean extends AuthCacheMBean
+ {
+ public static final String CACHE_NAME = "JmxPermissionsCache";
+ @Deprecated
+ public static final String DEPRECATED_CACHE_NAME =
"JMXPermissionsCache";
+
+ public void invalidatePermissions(String roleName);
}
}
diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java
b/src/java/org/apache/cassandra/tools/NodeProbe.java
index e7c595e..d1b7528 100644
--- a/src/java/org/apache/cassandra/tools/NodeProbe.java
+++ b/src/java/org/apache/cassandra/tools/NodeProbe.java
@@ -55,12 +55,20 @@ import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.rmi.ssl.SslRMIClientSocketFactory;
-
import org.apache.cassandra.audit.AuditLogManager;
import org.apache.cassandra.audit.AuditLogManagerMBean;
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.NetworkPermissionsCache;
+import org.apache.cassandra.auth.NetworkPermissionsCacheMBean;
+import org.apache.cassandra.auth.PasswordAuthenticator;
+import org.apache.cassandra.auth.PermissionsCache;
+import org.apache.cassandra.auth.PermissionsCacheMBean;
+import org.apache.cassandra.auth.RolesCache;
+import org.apache.cassandra.auth.RolesCacheMBean;
+import org.apache.cassandra.auth.jmx.AuthorizationProxy;
import org.apache.cassandra.batchlog.BatchlogManager;
import org.apache.cassandra.batchlog.BatchlogManagerMBean;
import org.apache.cassandra.db.ColumnFamilyStoreMBean;
@@ -140,6 +148,11 @@ public class NodeProbe implements AutoCloseable
protected BatchlogManagerMBean bmProxy;
protected ActiveRepairServiceMBean arsProxy;
protected AuditLogManagerMBean almProxy;
+ protected PasswordAuthenticator.CredentialsCacheMBean ccProxy;
+ protected AuthorizationProxy.JmxPermissionsCacheMBean jpcProxy;
+ protected NetworkPermissionsCacheMBean npcProxy;
+ protected PermissionsCacheMBean pcProxy;
+ protected RolesCacheMBean rcProxy;
protected Output output;
private boolean failed;
@@ -248,6 +261,16 @@ public class NodeProbe implements AutoCloseable
arsProxy = JMX.newMBeanProxy(mbeanServerConn, name,
ActiveRepairServiceMBean.class);
name = new ObjectName(AuditLogManager.MBEAN_NAME);
almProxy = JMX.newMBeanProxy(mbeanServerConn, name,
AuditLogManagerMBean.class);
+ name = new ObjectName(AuthCache.MBEAN_NAME_BASE +
PasswordAuthenticator.CredentialsCacheMBean.CACHE_NAME);
+ ccProxy = JMX.newMBeanProxy(mbeanServerConn, name,
PasswordAuthenticator.CredentialsCacheMBean.class);
+ name = new ObjectName(AuthCache.MBEAN_NAME_BASE +
AuthorizationProxy.JmxPermissionsCacheMBean.CACHE_NAME);
+ jpcProxy = JMX.newMBeanProxy(mbeanServerConn, name,
AuthorizationProxy.JmxPermissionsCacheMBean.class);
+ name = new ObjectName(AuthCache.MBEAN_NAME_BASE +
NetworkPermissionsCache.CACHE_NAME);
+ npcProxy = JMX.newMBeanProxy(mbeanServerConn, name,
NetworkPermissionsCacheMBean.class);
+ name = new ObjectName(AuthCache.MBEAN_NAME_BASE +
PermissionsCache.CACHE_NAME);
+ pcProxy = JMX.newMBeanProxy(mbeanServerConn, name,
PermissionsCacheMBean.class);
+ name = new ObjectName(AuthCache.MBEAN_NAME_BASE +
RolesCache.CACHE_NAME);
+ rcProxy = JMX.newMBeanProxy(mbeanServerConn, name,
RolesCacheMBean.class);
}
catch (MalformedObjectNameException e)
{
@@ -481,11 +504,61 @@ public class NodeProbe implements AutoCloseable
cacheService.invalidateCounterCache();
}
+ public void invalidateCredentialsCache()
+ {
+ ccProxy.invalidate();
+ }
+
+ public void invalidateCredentialsCache(String roleName)
+ {
+ ccProxy.invalidateCredentials(roleName);
+ }
+
+ public void invalidateJmxPermissionsCache()
+ {
+ jpcProxy.invalidate();
+ }
+
+ public void invalidateJmxPermissionsCache(String roleName)
+ {
+ jpcProxy.invalidatePermissions(roleName);
+ }
+
public void invalidateKeyCache()
{
cacheService.invalidateKeyCache();
}
+ public void invalidateNetworkPermissionsCache()
+ {
+ npcProxy.invalidate();
+ }
+
+ public void invalidateNetworkPermissionsCache(String roleName)
+ {
+ npcProxy.invalidateNetworkPermissions(roleName);
+ }
+
+ public void invalidatePermissionsCache()
+ {
+ pcProxy.invalidate();
+ }
+
+ public void invalidatePermissionsCache(String userName, String
resourceName)
+ {
+ pcProxy.invalidatePermissions(userName, resourceName);
+ }
+
+ public void invalidateRolesCache()
+ {
+ rcProxy.invalidate();
+ }
+
+ public void invalidateRolesCache(String roleName)
+ {
+ rcProxy.invalidateRoles(roleName);
+ }
+
public void invalidateRowCache()
{
cacheService.invalidateRowCache();
diff --git a/src/java/org/apache/cassandra/tools/NodeTool.java
b/src/java/org/apache/cassandra/tools/NodeTool.java
index 544bab0..1f5295f 100644
--- a/src/java/org/apache/cassandra/tools/NodeTool.java
+++ b/src/java/org/apache/cassandra/tools/NodeTool.java
@@ -33,7 +33,6 @@ import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOError;
import java.io.IOException;
-import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -140,9 +139,14 @@ public class NodeTool
GetMaxHintWindow.class,
GossipInfo.class,
Import.class,
+ InvalidateCounterCache.class,
+ InvalidateCredentialsCache.class,
+ InvalidateJmxPermissionsCache.class,
InvalidateKeyCache.class,
+ InvalidateNetworkPermissionsCache.class,
+ InvalidatePermissionsCache.class,
+ InvalidateRolesCache.class,
InvalidateRowCache.class,
- InvalidateCounterCache.class,
Join.class,
Move.class,
PauseHandoff.class,
@@ -473,29 +477,6 @@ public class NodeTool
}
}
- public static SortedMap<String, SetHostStat> getOwnershipByDc(NodeProbe
probe, boolean resolveIp,
- Map<String,
String> tokenToEndpoint,
-
Map<InetAddress, Float> ownerships)
- {
- SortedMap<String, SetHostStat> ownershipByDc = Maps.newTreeMap();
- EndpointSnitchInfoMBean epSnitchInfo =
probe.getEndpointSnitchInfoProxy();
- try
- {
- for (Entry<String, String> tokenAndEndPoint :
tokenToEndpoint.entrySet())
- {
- String dc =
epSnitchInfo.getDatacenter(tokenAndEndPoint.getValue());
- if (!ownershipByDc.containsKey(dc))
- ownershipByDc.put(dc, new SetHostStat(resolveIp));
- ownershipByDc.get(dc).add(tokenAndEndPoint.getKey(),
tokenAndEndPoint.getValue(), ownerships);
- }
- }
- catch (UnknownHostException e)
- {
- throw new RuntimeException(e);
- }
- return ownershipByDc;
- }
-
public static SortedMap<String, SetHostStatWithPort>
getOwnershipByDcWithPort(NodeProbe probe, boolean resolveIp,
Map<String,
String> tokenToEndpoint,
Map<String,
Float> ownerships)
diff --git
a/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java
new file mode 100644
index 0000000..0f9079f
--- /dev/null
+++
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCache.java
@@ -0,0 +1,49 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import io.airlift.airline.Arguments;
+import io.airlift.airline.Command;
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+@Command(name = "invalidatecredentialscache", description = "Invalidate the
credentials cache")
+public class InvalidateCredentialsCache extends NodeToolCmd
+{
+ @Arguments(usage = "[<role>...]", description = "List of roles to
invalidate. By default, all roles")
+ private List<String> args = new ArrayList<>();
+
+ @Override
+ public void execute(NodeProbe probe)
+ {
+ if (args.isEmpty())
+ {
+ probe.invalidateCredentialsCache();
+ }
+ else
+ {
+ for (String roleName : args)
+ {
+ probe.invalidateCredentialsCache(roleName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git
a/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java
new file mode 100644
index 0000000..c242b03
--- /dev/null
+++
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCache.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import io.airlift.airline.Arguments;
+import io.airlift.airline.Command;
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+@Command(name = "invalidatejmxpermissionscache", description = "Invalidate the
JMX permissions cache")
+public class InvalidateJmxPermissionsCache extends NodeToolCmd
+{
+ @Arguments(usage = "[<role>...]", description = "List of roles to
invalidate. By default, all roles")
+ private List<String> args = new ArrayList<>();
+
+ @Override
+ public void execute(NodeProbe probe)
+ {
+ if (args.isEmpty())
+ {
+ probe.invalidateJmxPermissionsCache();
+ } else
+ {
+ for (String roleName : args)
+ {
+ probe.invalidateJmxPermissionsCache(roleName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git
a/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java
new file mode 100644
index 0000000..8b58060
--- /dev/null
+++
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCache.java
@@ -0,0 +1,49 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import io.airlift.airline.Arguments;
+import io.airlift.airline.Command;
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+@Command(name = "invalidatenetworkpermissionscache", description = "Invalidate
the network permissions cache")
+public class InvalidateNetworkPermissionsCache extends NodeToolCmd
+{
+ @Arguments(usage = "[<role>...]", description = "List of roles to
invalidate. By default, all roles")
+ private List<String> args = new ArrayList<>();
+
+ @Override
+ public void execute(NodeProbe probe)
+ {
+ if (args.isEmpty())
+ {
+ probe.invalidateNetworkPermissionsCache();
+ }
+ else
+ {
+ for (String roleName : args)
+ {
+ probe.invalidateNetworkPermissionsCache(roleName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git
a/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java
b/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java
new file mode 100644
index 0000000..049e91b
--- /dev/null
+++
b/src/java/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCache.java
@@ -0,0 +1,174 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import io.airlift.airline.Arguments;
+import io.airlift.airline.Command;
+import io.airlift.airline.Option;
+import org.apache.cassandra.auth.DataResource;
+import org.apache.cassandra.auth.FunctionResource;
+import org.apache.cassandra.auth.JMXResource;
+import org.apache.cassandra.auth.RoleResource;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@Command(name = "invalidatepermissionscache", description = "Invalidate the
permissions cache")
+public class InvalidatePermissionsCache extends NodeToolCmd
+{
+ @Arguments(usage = "[<user>]", description = "A specific user for whom
permissions need to be invalidated")
+ private List<String> args = new ArrayList<>();
+
+ // Data Resources
+ @Option(title = "all-keyspaces",
+ name = {"--all-keyspaces"},
+ description = "Invalidate permissions for 'ALL KEYSPACES'")
+ private boolean allKeyspaces;
+
+ @Option(title = "keyspace",
+ name = {"--keyspace"},
+ description = "Keyspace to invalidate permissions for")
+ private String keyspace;
+
+ @Option(title = "table",
+ name = {"--table"},
+ description = "Table to invalidate permissions for (you must
specify --keyspace for using this option)")
+ private String table;
+
+ // Roles Resources
+ @Option(title = "all-roles",
+ name = {"--all-roles"},
+ description = "Invalidate permissions for 'ALL ROLES'")
+ private boolean allRoles;
+
+ @Option(title = "role",
+ name = {"--role"},
+ description = "Role to invalidate permissions for")
+ private String role;
+
+ // Functions Resources
+ @Option(title = "all-functions",
+ name = {"--all-functions"},
+ description = "Invalidate permissions for 'ALL FUNCTIONS'")
+ private boolean allFunctions;
+
+ @Option(title = "functions-in-keyspace",
+ name = {"--functions-in-keyspace"},
+ description = "Keyspace to invalidate permissions for")
+ private String functionsInKeyspace;
+
+ @Option(title = "function",
+ name = {"--function"},
+ description = "Function to invalidate permissions for (you must
specify --functions-in-keyspace for using " +
+ "this option; function format: name[arg1^..^agrN], for
example: foo[Int32Type^DoubleType])")
+ private String function;
+
+ // MBeans Resources
+ @Option(title = "all-mbeans",
+ name = {"--all-mbeans"},
+ description = "Invalidate permissions for 'ALL MBEANS'")
+ private boolean allMBeans;
+
+ @Option(title = "mbean",
+ name = {"--mbean"},
+ description = "MBean to invalidate permissions for")
+ private String mBean;
+
+ @Override
+ public void execute(NodeProbe probe)
+ {
+ if (args.isEmpty())
+ {
+ checkArgument(!allKeyspaces && StringUtils.isEmpty(keyspace) &&
StringUtils.isEmpty(table)
+ && !allRoles && StringUtils.isEmpty(role)
+ && !allFunctions &&
StringUtils.isEmpty(functionsInKeyspace) && StringUtils.isEmpty(function)
+ && !allMBeans && StringUtils.isEmpty(mBean),
+ "No options allowed without a <user> being specified");
+
+ probe.invalidatePermissionsCache();
+ }
+ else
+ {
+ checkArgument(args.size() == 1,
+ "A single <user> is only supported / you have a typo in
the options spelling");
+ List<String> resourceNames = new ArrayList<>();
+
+ // Data Resources
+ if (allKeyspaces)
+ resourceNames.add(DataResource.root().getName());
+
+ if (StringUtils.isNotEmpty(table))
+ if (StringUtils.isNotEmpty(keyspace))
+ resourceNames.add(DataResource.table(keyspace,
table).getName());
+ else
+ throw new IllegalArgumentException("--table option should
be passed along with --keyspace option");
+ else
+ if (StringUtils.isNotEmpty(keyspace))
+
resourceNames.add(DataResource.keyspace(keyspace).getName());
+
+ // Roles Resources
+ if (allRoles)
+ resourceNames.add(RoleResource.root().getName());
+
+ if (StringUtils.isNotEmpty(role))
+ resourceNames.add(RoleResource.role(role).getName());
+
+ // Function Resources
+ if (allFunctions)
+ resourceNames.add(FunctionResource.root().getName());
+
+ if (StringUtils.isNotEmpty(function))
+ if (StringUtils.isNotEmpty(functionsInKeyspace))
+
resourceNames.add(constructFunctionResource(functionsInKeyspace, function));
+ else
+ throw new IllegalArgumentException("--function option
should be passed along with --functions-in-keyspace option");
+ else
+ if (StringUtils.isNotEmpty(functionsInKeyspace))
+
resourceNames.add(FunctionResource.keyspace(functionsInKeyspace).getName());
+
+ // MBeans Resources
+ if (allMBeans)
+ resourceNames.add(JMXResource.root().getName());
+
+ if (StringUtils.isNotEmpty(mBean))
+ resourceNames.add(JMXResource.mbean(mBean).getName());
+
+ String userName = args.get(0);
+
+ for (String resourceName : resourceNames)
+ probe.invalidatePermissionsCache(userName, resourceName);
+ }
+ }
+
+ private String constructFunctionResource(String functionsInKeyspace,
String function) {
+ try
+ {
+ return FunctionResource.fromName("functions/" +
functionsInKeyspace + '/' + function).getName();
+ } catch (ConfigurationException e)
+ {
+ throw new IllegalArgumentException("An error was encountered when
looking up function definition; " + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git
a/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java
b/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java
new file mode 100644
index 0000000..4fca5c3
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/InvalidateRolesCache.java
@@ -0,0 +1,50 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import io.airlift.airline.Arguments;
+import io.airlift.airline.Command;
+import org.apache.cassandra.tools.NodeProbe;
+import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
+
+@Command(name = "invalidaterolescache", description = "Invalidate the roles
cache")
+public class InvalidateRolesCache extends NodeToolCmd
+{
+
+ @Arguments(usage = "[<role>...]", description = "List of roles to
invalidate. By default, all roles")
+ private List<String> args = new ArrayList<>();
+
+ @Override
+ public void execute(NodeProbe probe)
+ {
+ if (args.isEmpty())
+ {
+ probe.invalidateRolesCache();
+ }
+ else
+ {
+ for (String roleName : args)
+ {
+ probe.invalidateRolesCache(roleName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/unit/org/apache/cassandra/auth/RoleTestUtils.java
b/test/unit/org/apache/cassandra/auth/AuthTestUtils.java
similarity index 60%
rename from test/unit/org/apache/cassandra/auth/RoleTestUtils.java
rename to test/unit/org/apache/cassandra/auth/AuthTestUtils.java
index e2d1006..a012b62 100644
--- a/test/unit/org/apache/cassandra/auth/RoleTestUtils.java
+++ b/test/unit/org/apache/cassandra/auth/AuthTestUtils.java
@@ -23,16 +23,18 @@ import java.util.concurrent.Callable;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
+import org.apache.cassandra.cql3.statements.BatchStatement;
import org.apache.cassandra.cql3.statements.SelectStatement;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.messages.ResultMessage;
-public class RoleTestUtils
+public class AuthTestUtils
{
public static final RoleResource ROLE_A = RoleResource.role("role_a");
@@ -71,15 +73,78 @@ public class RoleTestUtils
}
}
+ public static class LocalCassandraAuthorizer extends CassandraAuthorizer
+ {
+ ResultMessage.Rows select(SelectStatement statement, QueryOptions
options)
+ {
+ return statement.executeLocally(QueryState.forInternalCalls(),
options);
+ }
+
+ UntypedResultSet process(String query) throws RequestExecutionException
+ {
+ return QueryProcessor.executeInternal(query);
+ }
+
+ @Override
+ void processBatch(BatchStatement statement)
+ {
+ statement.executeLocally(QueryState.forInternalCalls(),
QueryOptions.DEFAULT);
+ }
+ }
+
+ public static class LocalCassandraNetworkAuthorizer extends
CassandraNetworkAuthorizer
+ {
+ ResultMessage.Rows select(SelectStatement statement, QueryOptions
options)
+ {
+ return statement.executeLocally(QueryState.forInternalCalls(),
options);
+ }
+
+ void process(String query)
+ {
+ QueryProcessor.executeInternal(query);
+ }
+ }
+
+ public static class LocalPasswordAuthenticator extends
PasswordAuthenticator
+ {
+ ResultMessage.Rows select(SelectStatement statement, QueryOptions
options)
+ {
+ return statement.executeLocally(QueryState.forInternalCalls(),
options);
+ }
+ }
+
public static void grantRolesTo(IRoleManager roleManager, RoleResource
grantee, RoleResource...granted)
{
for(RoleResource toGrant : granted)
roleManager.grantRole(AuthenticatedUser.ANONYMOUS_USER, toGrant,
grantee);
}
- public static long getReadCount()
+ public static long getNetworkPermissionsReadCount()
+ {
+ ColumnFamilyStore networkPemissionsTable =
+
Keyspace.open(SchemaConstants.AUTH_KEYSPACE_NAME).getColumnFamilyStore(AuthKeyspace.NETWORK_PERMISSIONS);
+ return networkPemissionsTable.metric.readLatency.latency.getCount();
+ }
+
+ public static long getRolePermissionsReadCount()
+ {
+ ColumnFamilyStore rolesPemissionsTable =
+
Keyspace.open(SchemaConstants.AUTH_KEYSPACE_NAME).getColumnFamilyStore(AuthKeyspace.ROLE_PERMISSIONS);
+ return rolesPemissionsTable.metric.readLatency.latency.getCount();
+ }
+
+ public static long getRolesReadCount()
{
ColumnFamilyStore rolesTable =
Keyspace.open(SchemaConstants.AUTH_KEYSPACE_NAME).getColumnFamilyStore(AuthKeyspace.ROLES);
return rolesTable.metric.readLatency.latency.getCount();
}
+
+ public static RoleOptions getLoginRoleOprions()
+ {
+ RoleOptions roleOptions = new RoleOptions();
+ roleOptions.setOption(IRoleManager.Option.SUPERUSER, false);
+ roleOptions.setOption(IRoleManager.Option.LOGIN, true);
+ roleOptions.setOption(IRoleManager.Option.PASSWORD, "ignored");
+ return roleOptions;
+ }
}
\ No newline at end of file
diff --git
a/test/unit/org/apache/cassandra/auth/CassandraNetworkAuthorizerTest.java
b/test/unit/org/apache/cassandra/auth/CassandraNetworkAuthorizerTest.java
index 31af270..3dc9d91 100644
--- a/test/unit/org/apache/cassandra/auth/CassandraNetworkAuthorizerTest.java
+++ b/test/unit/org/apache/cassandra/auth/CassandraNetworkAuthorizerTest.java
@@ -31,62 +31,26 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLStatement;
-import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.statements.AlterRoleStatement;
import org.apache.cassandra.cql3.statements.AuthenticationStatement;
-import org.apache.cassandra.cql3.statements.BatchStatement;
import org.apache.cassandra.cql3.statements.CreateRoleStatement;
import org.apache.cassandra.cql3.statements.DropRoleStatement;
-import org.apache.cassandra.cql3.statements.SelectStatement;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
-import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.service.ClientState;
-import org.apache.cassandra.service.QueryState;
-import org.apache.cassandra.transport.messages.ResultMessage;
import static org.apache.cassandra.auth.AuthKeyspace.NETWORK_PERMISSIONS;
-import static
org.apache.cassandra.auth.RoleTestUtils.LocalCassandraRoleManager;
+import static org.apache.cassandra.auth.AuthTestUtils.LocalCassandraAuthorizer;
+import static
org.apache.cassandra.auth.AuthTestUtils.LocalCassandraNetworkAuthorizer;
+import static
org.apache.cassandra.auth.AuthTestUtils.LocalCassandraRoleManager;
+import static org.apache.cassandra.auth.AuthTestUtils.getRolesReadCount;
import static org.apache.cassandra.schema.SchemaConstants.AUTH_KEYSPACE_NAME;
-import static org.apache.cassandra.auth.RoleTestUtils.getReadCount;
public class CassandraNetworkAuthorizerTest
{
- private static class LocalCassandraAuthorizer extends CassandraAuthorizer
- {
- ResultMessage.Rows select(SelectStatement statement, QueryOptions
options)
- {
- return statement.executeLocally(QueryState.forInternalCalls(),
options);
- }
-
- UntypedResultSet process(String query) throws RequestExecutionException
- {
- return QueryProcessor.executeInternal(query);
- }
-
- @Override
- void processBatch(BatchStatement statement)
- {
- statement.executeLocally(QueryState.forInternalCalls(),
QueryOptions.DEFAULT);
- }
- }
-
- private static class LocalCassandraNetworkAuthorizer extends
CassandraNetworkAuthorizer
- {
- ResultMessage.Rows select(SelectStatement statement, QueryOptions
options)
- {
- return statement.executeLocally(QueryState.forInternalCalls(),
options);
- }
-
- void process(String query)
- {
- QueryProcessor.executeInternal(query);
- }
- }
-
private static void setupSuperUser()
{
QueryProcessor.executeInternal(String.format("INSERT INTO %s.%s (role,
is_superuser, can_login, salted_hash) "
@@ -249,10 +213,10 @@ public class CassandraNetworkAuthorizerTest
{
String username = createName();
auth("CREATE ROLE %s", username);
- long readCount = getReadCount();
+ long readCount = getRolesReadCount();
dcPerms(username);
- Assert.assertEquals(++readCount, getReadCount());
+ Assert.assertEquals(++readCount, getRolesReadCount());
dcPerms(username);
- Assert.assertEquals(readCount, getReadCount());
+ Assert.assertEquals(readCount, getRolesReadCount());
}
}
diff --git a/test/unit/org/apache/cassandra/auth/CassandraRoleManagerTest.java
b/test/unit/org/apache/cassandra/auth/CassandraRoleManagerTest.java
index 6583c49..ea1ff65 100644
--- a/test/unit/org/apache/cassandra/auth/CassandraRoleManagerTest.java
+++ b/test/unit/org/apache/cassandra/auth/CassandraRoleManagerTest.java
@@ -29,7 +29,7 @@ import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
-import static org.apache.cassandra.auth.RoleTestUtils.*;
+import static org.apache.cassandra.auth.AuthTestUtils.*;
import static org.junit.Assert.assertEquals;
public class CassandraRoleManagerTest
@@ -80,9 +80,9 @@ public class CassandraRoleManagerTest
private void fetchRolesAndCheckReadCount(IRoleManager roleManager,
RoleResource primaryRole)
{
- long before = getReadCount();
+ long before = getRolesReadCount();
Set<Role> granted = roleManager.getRoleDetails(primaryRole);
- long after = getReadCount();
+ long after = getRolesReadCount();
assertEquals(granted.size(), after - before);
}
}
diff --git a/test/unit/org/apache/cassandra/auth/RolesTest.java
b/test/unit/org/apache/cassandra/auth/RolesTest.java
index 94322a7..0211d46 100644
--- a/test/unit/org/apache/cassandra/auth/RolesTest.java
+++ b/test/unit/org/apache/cassandra/auth/RolesTest.java
@@ -29,7 +29,7 @@ import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
-import static org.apache.cassandra.auth.RoleTestUtils.*;
+import static org.apache.cassandra.auth.AuthTestUtils.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -57,39 +57,39 @@ public class RolesTest
public void superuserStatusIsCached()
{
boolean hasSuper = Roles.hasSuperuserStatus(ROLE_A);
- long count = getReadCount();
+ long count = getRolesReadCount();
assertEquals(hasSuper, Roles.hasSuperuserStatus(ROLE_A));
- assertEquals(count, getReadCount());
+ assertEquals(count, getRolesReadCount());
}
@Test
public void loginPrivilegeIsCached()
{
boolean canLogin = Roles.canLogin(ROLE_A);
- long count = getReadCount();
+ long count = getRolesReadCount();
assertEquals(canLogin, Roles.canLogin(ROLE_A));
- assertEquals(count, getReadCount());
+ assertEquals(count, getRolesReadCount());
}
@Test
public void grantedRoleDetailsAreCached()
{
Iterable<Role> granted = Roles.getRoleDetails(ROLE_A);
- long count = getReadCount();
+ long count = getRolesReadCount();
assertTrue(Iterables.elementsEqual(granted,
Roles.getRoleDetails(ROLE_A)));
- assertEquals(count, getReadCount());
+ assertEquals(count, getRolesReadCount());
}
@Test
public void grantedRoleResourcesAreCached()
{
Set<RoleResource> granted = Roles.getRoles(ROLE_A);
- long count = getReadCount();
+ long count = getRolesReadCount();
assertEquals(granted, Roles.getRoles(ROLE_A));
- assertEquals(count, getReadCount());
+ assertEquals(count, getRolesReadCount());
}
}
diff --git
a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java
new file mode 100644
index 0000000..7ed5530
--- /dev/null
+++
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateCredentialsCacheTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.junit.runner.RunWith;
+
+import com.datastax.driver.core.EndPoint;
+import com.datastax.driver.core.PlainTextAuthProvider;
+import org.apache.cassandra.OrderedJUnit4ClassRunner;
+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.PasswordAuthenticator;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
+import static org.apache.cassandra.auth.AuthTestUtils.getRolesReadCount;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(OrderedJUnit4ClassRunner.class)
+public class InvalidateCredentialsCacheTest extends CQLTester
+{
+ private static IAuthenticator.SaslNegotiator roleANegotiator;
+ private static IAuthenticator.SaslNegotiator roleBNegotiator;
+
+ @BeforeClass
+ public static void setup() throws Exception
+ {
+ SchemaLoader.prepareServer();
+ AuthTestUtils.LocalCassandraRoleManager roleManager = new
AuthTestUtils.LocalCassandraRoleManager();
+ PasswordAuthenticator authenticator = new
AuthTestUtils.LocalPasswordAuthenticator();
+ SchemaLoader.setupAuth(roleManager,
+ authenticator,
+ new AuthTestUtils.LocalCassandraAuthorizer(),
+ new AuthTestUtils.LocalCassandraNetworkAuthorizer());
+
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A,
AuthTestUtils.getLoginRoleOprions());
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B,
AuthTestUtils.getLoginRoleOprions());
+
+ roleANegotiator = authenticator.newSaslNegotiator(null);
+ roleANegotiator.evaluateResponse(new
PlainTextAuthProvider(ROLE_A.getRoleName(), "ignored")
+ .newAuthenticator((EndPoint) null, null)
+ .initialResponse());
+ roleBNegotiator = authenticator.newSaslNegotiator(null);
+ roleBNegotiator.evaluateResponse(new
PlainTextAuthProvider(ROLE_B.getRoleName(), "ignored")
+ .newAuthenticator((EndPoint) null, null)
+ .initialResponse());
+
+ 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",
"invalidatecredentialscache");
+ tool.assertOnCleanExit();
+
+ String help = "NAME\n" +
+ " nodetool invalidatecredentialscache -
Invalidate the credentials 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>)] invalidatecredentialscache\n" +
+ " [--] [<role>...]\n" +
+ "\n" +
+ "OPTIONS\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" +
+ " This option can be used to separate
command-line options from the\n" +
+ " list of argument, (useful when arguments
might be mistaken for\n" +
+ " command-line options\n" +
+ "\n" +
+ " [<role>...]\n" +
+ " List of roles to invalidate. By default,
all roles\n" +
+ "\n" +
+ "\n";
+ assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testInvalidateSingleCredential()
+ {
+ // cache credential
+ roleANegotiator.getAuthenticatedUser();
+ long originalReadsCount = getRolesReadCount();
+
+ // enure credential is cached
+ assertThat(roleANegotiator.getAuthenticatedUser()).isEqualTo(new
AuthenticatedUser(ROLE_A.getRoleName()));
+ assertThat(originalReadsCount).isEqualTo(getRolesReadCount());
+
+ // invalidate credential
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatecredentialscache", ROLE_A.getRoleName());
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure credential is reloaded
+ assertThat(roleANegotiator.getAuthenticatedUser()).isEqualTo(new
AuthenticatedUser(ROLE_A.getRoleName()));
+ assertThat(originalReadsCount).isLessThan(getRolesReadCount());
+ }
+
+ @Test
+ public void testInvalidateAllCredentials()
+ {
+ // cache credentials
+ roleANegotiator.getAuthenticatedUser();
+ roleBNegotiator.getAuthenticatedUser();
+ long originalReadsCount = getRolesReadCount();
+
+ // enure credentials are cached
+ assertThat(roleANegotiator.getAuthenticatedUser()).isEqualTo(new
AuthenticatedUser(ROLE_A.getRoleName()));
+ assertThat(roleBNegotiator.getAuthenticatedUser()).isEqualTo(new
AuthenticatedUser(ROLE_B.getRoleName()));
+ assertThat(originalReadsCount).isEqualTo(getRolesReadCount());
+
+ // invalidate both credentials
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatecredentialscache");
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure credential for roleA is reloaded
+ assertThat(roleANegotiator.getAuthenticatedUser()).isEqualTo(new
AuthenticatedUser(ROLE_A.getRoleName()));
+ long readsCountAfterFirstReLoad = getRolesReadCount();
+ assertThat(originalReadsCount).isLessThan(readsCountAfterFirstReLoad);
+
+ // ensure credential for roleB is reloaded
+ assertThat(roleBNegotiator.getAuthenticatedUser()).isEqualTo(new
AuthenticatedUser(ROLE_B.getRoleName()));
+ long readsCountAfterSecondReLoad = getRolesReadCount();
+
assertThat(readsCountAfterFirstReLoad).isLessThan(readsCountAfterSecondReLoad);
+ }
+}
diff --git
a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java
new file mode 100644
index 0000000..81fa2c5
--- /dev/null
+++
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateJmxPermissionsCacheTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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 java.util.Set;
+import javax.security.auth.Subject;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.cassandra.OrderedJUnit4ClassRunner;
+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.JMXResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.auth.jmx.AuthorizationProxy;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
+import static
org.apache.cassandra.auth.AuthTestUtils.getRolePermissionsReadCount;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(OrderedJUnit4ClassRunner.class)
+public class InvalidateJmxPermissionsCacheTest extends CQLTester
+{
+ private static final AuthorizationProxy authorizationProxy = new
NoAuthSetupAuthorizationProxy();
+
+ @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());
+
+ JMXResource rootJmxResource = JMXResource.root();
+ Set<Permission> jmxPermissions =
rootJmxResource.applicablePermissions();
+
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A,
AuthTestUtils.getLoginRoleOprions());
+ authorizer.grant(AuthenticatedUser.SYSTEM_USER, jmxPermissions,
rootJmxResource, ROLE_A);
+
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B,
AuthTestUtils.getLoginRoleOprions());
+ authorizer.grant(AuthenticatedUser.SYSTEM_USER, jmxPermissions,
rootJmxResource, ROLE_B);
+
+ 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",
"invalidatejmxpermissionscache");
+ tool.assertOnCleanExit();
+
+ String help = "NAME\n" +
+ " nodetool invalidatejmxpermissionscache -
Invalidate the JMX permissions\n" +
+ " 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>)] invalidatejmxpermissionscache\n" +
+ " [--] [<role>...]\n" +
+ "\n" +
+ "OPTIONS\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" +
+ " This option can be used to separate
command-line options from the\n" +
+ " list of argument, (useful when arguments
might be mistaken for\n" +
+ " command-line options\n" +
+ "\n" +
+ " [<role>...]\n" +
+ " List of roles to invalidate. By default,
all roles\n" +
+ "\n" +
+ "\n";
+ assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testInvalidateSingleJMXPermission()
+ {
+ Subject userSubject = subject(ROLE_A.getRoleName());
+
+ // cache role permission
+ authorizationProxy.authorize(userSubject, "queryNames", null);
+ long originalReadsCount = getRolePermissionsReadCount();
+
+ // enure role permission is cached
+ assertThat(authorizationProxy.authorize(userSubject, "queryNames",
null)).isTrue();
+
assertThat(originalReadsCount).isEqualTo(getRolePermissionsReadCount());
+
+ // invalidate role permission
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatejmxpermissionscache",
ROLE_A.getRoleName());
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure role permission is reloaded
+ assertThat(authorizationProxy.authorize(userSubject, "queryNames",
null)).isTrue();
+
assertThat(originalReadsCount).isLessThan(getRolePermissionsReadCount());
+ }
+
+ @Test
+ public void testInvalidateAllJMXPermissions()
+ {
+ Subject roleASubject = subject(ROLE_A.getRoleName());
+ Subject roleBSubject = subject(ROLE_B.getRoleName());
+
+ // cache role permissions
+ authorizationProxy.authorize(roleASubject, "queryNames", null);
+ authorizationProxy.authorize(roleBSubject, "queryNames", null);
+ long originalReadsCount = getRolePermissionsReadCount();
+
+ // enure role permissions are cached
+ assertThat(authorizationProxy.authorize(roleASubject, "queryNames",
null)).isTrue();
+ assertThat(authorizationProxy.authorize(roleBSubject, "queryNames",
null)).isTrue();
+
assertThat(originalReadsCount).isEqualTo(getRolePermissionsReadCount());
+
+ // invalidate both role permissions
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatejmxpermissionscache");
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure role permission for roleA is reloaded
+ assertThat(authorizationProxy.authorize(roleASubject, "queryNames",
null)).isTrue();
+ long readsCountAfterFirstReLoad = getRolePermissionsReadCount();
+ assertThat(originalReadsCount).isLessThan(readsCountAfterFirstReLoad);
+
+ // ensure role permission for roleB is reloaded
+ assertThat(authorizationProxy.authorize(roleBSubject, "queryNames",
null)).isTrue();
+ long readsCountAfterSecondReLoad = getRolePermissionsReadCount();
+
assertThat(readsCountAfterFirstReLoad).isLessThan(readsCountAfterSecondReLoad);
+ }
+
+ private static Subject subject(String roleName)
+ {
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new CassandraPrincipal(roleName));
+ return subject;
+ }
+
+ private static class NoAuthSetupAuthorizationProxy extends
AuthorizationProxy
+ {
+ public NoAuthSetupAuthorizationProxy()
+ {
+ super();
+ this.isAuthSetupComplete = () -> true;
+ }
+ }
+}
diff --git
a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java
new file mode 100644
index 0000000..cef29b3
--- /dev/null
+++
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateNetworkPermissionsCacheTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.junit.runner.RunWith;
+
+import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.auth.AuthTestUtils;
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
+import static
org.apache.cassandra.auth.AuthTestUtils.getNetworkPermissionsReadCount;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(OrderedJUnit4ClassRunner.class)
+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());
+
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A,
AuthTestUtils.getLoginRoleOprions());
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B,
AuthTestUtils.getLoginRoleOprions());
+
+ 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",
"invalidatenetworkpermissionscache");
+ tool.assertOnCleanExit();
+
+ String help = "NAME\n" +
+ " nodetool invalidatenetworkpermissionscache -
Invalidate the network\n" +
+ " permissions 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>)]\n" +
+ " invalidatenetworkpermissionscache
[--] [<role>...]\n" +
+ "\n" +
+ "OPTIONS\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" +
+ " This option can be used to separate
command-line options from the\n" +
+ " list of argument, (useful when arguments
might be mistaken for\n" +
+ " command-line options\n" +
+ "\n" +
+ " [<role>...]\n" +
+ " List of roles to invalidate. By default,
all roles\n" +
+ "\n" +
+ "\n";
+ assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testInvalidateSingleNetworkPermission()
+ {
+ AuthenticatedUser role = new AuthenticatedUser(ROLE_A.getRoleName());
+
+ // cache network permission
+ role.hasLocalAccess();
+ long originalReadsCount = getNetworkPermissionsReadCount();
+
+ // enure network permission is cached
+ assertThat(role.hasLocalAccess()).isTrue();
+
assertThat(originalReadsCount).isEqualTo(getNetworkPermissionsReadCount());
+
+ // invalidate network permission
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatenetworkpermissionscache",
ROLE_A.getRoleName());
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure network permission is reloaded
+ assertThat(role.hasLocalAccess()).isTrue();
+
assertThat(originalReadsCount).isLessThan(getNetworkPermissionsReadCount());
+ }
+
+ @Test
+ public void testInvalidateAllNetworkPermissions()
+ {
+ AuthenticatedUser roleA = new AuthenticatedUser(ROLE_A.getRoleName());
+ AuthenticatedUser roleB = new AuthenticatedUser(ROLE_B.getRoleName());
+
+ // cache network permissions
+ roleA.hasLocalAccess();
+ roleB.hasLocalAccess();
+ long originalReadsCount = getNetworkPermissionsReadCount();
+
+ // enure network permissions are cached
+ assertThat(roleA.hasLocalAccess()).isTrue();
+ assertThat(roleB.hasLocalAccess()).isTrue();
+
assertThat(originalReadsCount).isEqualTo(getNetworkPermissionsReadCount());
+
+ // invalidate both network permissions
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatenetworkpermissionscache");
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure network permission for roleA is reloaded
+ assertThat(roleA.hasLocalAccess()).isTrue();
+ long readsCountAfterFirstReLoad = getNetworkPermissionsReadCount();
+ assertThat(originalReadsCount).isLessThan(readsCountAfterFirstReLoad);
+
+ // ensure network permission for roleB is reloaded
+ assertThat(roleB.hasLocalAccess()).isTrue();
+ long readsCountAfterSecondReLoad = getNetworkPermissionsReadCount();
+
assertThat(readsCountAfterFirstReLoad).isLessThan(readsCountAfterSecondReLoad);
+ }
+}
diff --git
a/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java
new file mode 100644
index 0000000..caaabf2
--- /dev/null
+++
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidatePermissionsCacheTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.cassandra.OrderedJUnit4ClassRunner;
+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.IResource;
+import org.apache.cassandra.auth.JMXResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.auth.RoleResource;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
+import static
org.apache.cassandra.auth.AuthTestUtils.getRolePermissionsReadCount;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(OrderedJUnit4ClassRunner.class)
+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());
+
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A,
AuthTestUtils.getLoginRoleOprions());
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B,
AuthTestUtils.getLoginRoleOprions());
+
+ List<IResource> resources = Arrays.asList(
+ DataResource.root(),
+ DataResource.keyspace(KEYSPACE),
+ DataResource.table(KEYSPACE, "t1"),
+ RoleResource.root(),
+ RoleResource.role("role_x"),
+ FunctionResource.root(),
+ FunctionResource.keyspace(KEYSPACE),
+ // Particular function is excluded from here and covered by a
separate test because in order to grant
+ // permissions we need to have a function registered. However,
the function cannot be registered via
+ // CQLTester.createFunction from static contex. That's why we
initialize it in a separate test case.
+ JMXResource.root(),
+ JMXResource.mbean("org.apache.cassandra.auth:type=*"));
+
+ for (IResource resource : resources)
+ {
+ Set<Permission> permissions = resource.applicablePermissions();
+ authorizer.grant(AuthenticatedUser.SYSTEM_USER, permissions,
resource, ROLE_A);
+ authorizer.grant(AuthenticatedUser.SYSTEM_USER, permissions,
resource, ROLE_B);
+ }
+
+ 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",
"invalidatepermissionscache");
+ tool.assertOnCleanExit();
+
+ String help = "NAME\n" +
+ " nodetool invalidatepermissionscache -
Invalidate the permissions 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>)] invalidatepermissionscache\n" +
+ " [--all-functions] [--all-keyspaces]
[--all-mbeans] [--all-roles]\n" +
+ " [--function <function>]\n" +
+ " [--functions-in-keyspace
<functions-in-keyspace>]\n" +
+ " [--keyspace <keyspace>] [--mbean
<mbean>] [--role <role>]\n" +
+ " [--table <table>] [--] [<user>]\n" +
+ "\n" +
+ "OPTIONS\n" +
+ " --all-functions\n" +
+ " Invalidate permissions for 'ALL
FUNCTIONS'\n" +
+ "\n" +
+ " --all-keyspaces\n" +
+ " Invalidate permissions for 'ALL
KEYSPACES'\n" +
+ "\n" +
+ " --all-mbeans\n" +
+ " Invalidate permissions for 'ALL
MBEANS'\n" +
+ "\n" +
+ " --all-roles\n" +
+ " Invalidate permissions for 'ALL ROLES'\n"
+
+ "\n" +
+ " --function <function>\n" +
+ " Function to invalidate permissions for
(you must specify\n" +
+ " --functions-in-keyspace for using this
option; function format:\n" +
+ " name[arg1^..^agrN], for example:
foo[Int32Type^DoubleType])\n" +
+ "\n" +
+ " --functions-in-keyspace
<functions-in-keyspace>\n" +
+ " Keyspace to invalidate permissions for\n"
+
+ "\n" +
+ " -h <host>, --host <host>\n" +
+ " Node hostname or ip address\n" +
+ "\n" +
+ " --keyspace <keyspace>\n" +
+ " Keyspace to invalidate permissions for\n"
+
+ "\n" +
+ " --mbean <mbean>\n" +
+ " MBean to invalidate permissions for\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" +
+ " --role <role>\n" +
+ " Role to invalidate permissions for\n" +
+ "\n" +
+ " --table <table>\n" +
+ " Table to invalidate permissions for (you
must specify --keyspace for\n" +
+ " using this option)\n" +
+ "\n" +
+ " -u <username>, --username <username>\n" +
+ " Remote jmx agent username\n" +
+ "\n" +
+ " --\n" +
+ " This option can be used to separate
command-line options from the\n" +
+ " list of argument, (useful when arguments
might be mistaken for\n" +
+ " command-line options\n" +
+ "\n" +
+ " [<user>]\n" +
+ " A specific user for whom permissions need
to be invalidated\n" +
+ "\n" +
+ "\n";
+ assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testInvalidatePermissionsWithIncorrectParameters()
+ {
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatepermissionscache", "--all-keyspaces");
+ assertThat(tool.getExitCode()).isEqualTo(1);
+ assertThat(tool.getStdout())
+ .isEqualTo(wrapByDefaultNodetoolMessage("No options allowed
without a <user> being specified"));
+ assertThat(tool.getStderr()).isEmpty();
+
+ tool = ToolRunner.invokeNodetool("invalidatepermissionscache",
"user1", "--invalid-option");
+ assertThat(tool.getExitCode()).isEqualTo(1);
+ assertThat(tool.getStdout())
+ .isEqualTo(wrapByDefaultNodetoolMessage("A single <user> is
only supported / you have a typo in the options spelling"));
+ assertThat(tool.getStderr()).isEmpty();
+
+ tool = ToolRunner.invokeNodetool("invalidatepermissionscache",
"user1", "--table", "t1");
+ assertThat(tool.getExitCode()).isEqualTo(1);
+ assertThat(tool.getStdout())
+ .isEqualTo(wrapByDefaultNodetoolMessage("--table option should
be passed along with --keyspace option"));
+ assertThat(tool.getStderr()).isEmpty();
+
+ tool = ToolRunner.invokeNodetool("invalidatepermissionscache",
"user1", "--function", "f[Int32Type]");
+ assertThat(tool.getExitCode()).isEqualTo(1);
+ assertThat(tool.getStdout())
+ .isEqualTo(wrapByDefaultNodetoolMessage("--function option
should be passed along with --functions-in-keyspace option"));
+ assertThat(tool.getStderr()).isEmpty();
+
+ tool = ToolRunner.invokeNodetool("invalidatepermissionscache",
"user1", "--functions-in-keyspace",
+ 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'"));
+ assertThat(tool.getStderr()).isEmpty();
+ }
+
+ @Test
+ public void testInvalidatePermissionsForEveryResourceExceptFunction()
+ {
+ assertInvalidation(DataResource.root(),
Collections.singletonList("--all-keyspaces"));
+ assertInvalidation(DataResource.keyspace(KEYSPACE),
Arrays.asList("--keyspace", KEYSPACE));
+ assertInvalidation(DataResource.table(KEYSPACE, "t1"),
+ Arrays.asList("--keyspace", KEYSPACE, "--table", "t1"));
+ assertInvalidation(RoleResource.root(),
Collections.singletonList("--all-roles"));
+ assertInvalidation(RoleResource.role("role_x"),
Arrays.asList("--role", "role_x"));
+ assertInvalidation(FunctionResource.root(),
Collections.singletonList("--all-functions"));
+ assertInvalidation(FunctionResource.keyspace(KEYSPACE),
Arrays.asList("--functions-in-keyspace", KEYSPACE));
+ assertInvalidation(JMXResource.root(),
Collections.singletonList("--all-mbeans"));
+
assertInvalidation(JMXResource.mbean("org.apache.cassandra.auth:type=*"),
+ Arrays.asList("--mbean", "org.apache.cassandra.auth:type=*"));
+ }
+
+ @Test
+ public void testInvalidatePermissionsForFunction() throws Throwable
+ {
+ String keyspaceAndFunctionName = createFunction(KEYSPACE, "int",
+ " CREATE FUNCTION %s (val int)" +
+ " CALLED ON NULL INPUT" +
+ " RETURNS int" +
+ " LANGUAGE java" +
+ " AS 'return val;'");
+ String functionName = StringUtils.split(keyspaceAndFunctionName,
".")[1];
+
+ FunctionResource resource = FunctionResource.function(KEYSPACE,
functionName, Collections.singletonList(Int32Type.instance));
+ Set<Permission> permissions = resource.applicablePermissions();
+
DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER,
permissions, resource, ROLE_A);
+
DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER,
permissions, resource, ROLE_B);
+
+ assertInvalidation(resource,
+ Arrays.asList("--functions-in-keyspace", KEYSPACE,
"--function", functionName + "[Int32Type]"));
+ }
+
+ private void assertInvalidation(IResource resource, List<String> options)
+ {
+ Set<Permission> dataPermissions = resource.applicablePermissions();
+
+ AuthenticatedUser role = new AuthenticatedUser(ROLE_A.getRoleName());
+
+ // cache permission
+ role.getPermissions(resource);
+ long originalReadsCount = getRolePermissionsReadCount();
+
+ // enure permission is cached
+ assertThat(role.getPermissions(resource)).isEqualTo(dataPermissions);
+
assertThat(originalReadsCount).isEqualTo(getRolePermissionsReadCount());
+
+ // invalidate permission
+ List<String> args = new ArrayList<>();
+ args.add("invalidatepermissionscache");
+ args.add(ROLE_A.getRoleName());
+ args.addAll(options);
+ ToolRunner.ToolResult tool = ToolRunner.invokeNodetool(args);
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure permission is reloaded
+ assertThat(role.getPermissions(resource)).isEqualTo(dataPermissions);
+
assertThat(originalReadsCount).isLessThan(getRolePermissionsReadCount());
+ }
+
+ @Test
+ public void testInvalidatePermissionsForAllUsers()
+ {
+ DataResource rootDataResource = DataResource.root();
+ Set<Permission> dataPermissions =
rootDataResource.applicablePermissions();
+
+ AuthenticatedUser roleA = new AuthenticatedUser(ROLE_A.getRoleName());
+ AuthenticatedUser roleB = new AuthenticatedUser(ROLE_B.getRoleName());
+
+ // cache permissions
+ roleA.getPermissions(rootDataResource);
+ roleB.getPermissions(rootDataResource);
+ long originalReadsCount = getRolePermissionsReadCount();
+
+ // enure permissions are cached
+
assertThat(roleA.getPermissions(rootDataResource)).isEqualTo(dataPermissions);
+
assertThat(roleB.getPermissions(rootDataResource)).isEqualTo(dataPermissions);
+
assertThat(originalReadsCount).isEqualTo(getRolePermissionsReadCount());
+
+ // invalidate both permissions
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidatepermissionscache");
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure permission for roleA is reloaded
+
assertThat(roleA.getPermissions(rootDataResource)).isEqualTo(dataPermissions);
+ long readsCountAfterFirstReLoad = getRolePermissionsReadCount();
+ assertThat(originalReadsCount).isLessThan(readsCountAfterFirstReLoad);
+
+ // ensure permission for roleB is reloaded
+
assertThat(roleB.getPermissions(rootDataResource)).isEqualTo(dataPermissions);
+ long readsCountAfterSecondReLoad = getRolePermissionsReadCount();
+
assertThat(readsCountAfterFirstReLoad).isLessThan(readsCountAfterSecondReLoad);
+ }
+
+ private String wrapByDefaultNodetoolMessage(String s)
+ {
+ return "nodetool: " + s + "\nSee 'nodetool help' or 'nodetool help
<command>'.\n";
+ }
+}
diff --git
a/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java
new file mode 100644
index 0000000..80596b3
--- /dev/null
+++
b/test/unit/org/apache/cassandra/tools/nodetool/InvalidateRolesCacheTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.junit.runner.RunWith;
+
+import org.apache.cassandra.OrderedJUnit4ClassRunner;
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.auth.AuthTestUtils;
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
+import static org.apache.cassandra.auth.AuthTestUtils.getRolesReadCount;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(OrderedJUnit4ClassRunner.class)
+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());
+
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A,
AuthTestUtils.getLoginRoleOprions());
+ roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B,
AuthTestUtils.getLoginRoleOprions());
+
+ 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",
"invalidaterolescache");
+ tool.assertOnCleanExit();
+
+ String help = "NAME\n" +
+ " nodetool invalidaterolescache - Invalidate
the roles 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>)] invalidaterolescache [--]\n" +
+ " [<role>...]\n" +
+ "\n" +
+ "OPTIONS\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" +
+ " This option can be used to separate
command-line options from the\n" +
+ " list of argument, (useful when arguments
might be mistaken for\n" +
+ " command-line options\n" +
+ "\n" +
+ " [<role>...]\n" +
+ " List of roles to invalidate. By default,
all roles\n" +
+ "\n" +
+ "\n";
+ assertThat(tool.getStdout()).isEqualTo(help);
+ }
+
+ @Test
+ public void testInvalidateSingleRole()
+ {
+ AuthenticatedUser role = new AuthenticatedUser(ROLE_A.getRoleName());
+
+ // cache role
+ role.canLogin();
+ long originalReadsCount = getRolesReadCount();
+
+ // enure role is cached
+ assertThat(role.canLogin()).isTrue();
+ assertThat(originalReadsCount).isEqualTo(getRolesReadCount());
+
+ // invalidate role
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidaterolescache", ROLE_A.getRoleName());
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure role is reloaded
+ assertThat(role.canLogin()).isTrue();
+ assertThat(originalReadsCount).isLessThan(getRolesReadCount());
+ }
+
+ @Test
+ public void testInvalidateAllRoles()
+ {
+ AuthenticatedUser roleA = new AuthenticatedUser(ROLE_A.getRoleName());
+ AuthenticatedUser roleB = new AuthenticatedUser(ROLE_B.getRoleName());
+
+ // cache roles
+ roleA.canLogin();
+ roleB.canLogin();
+ long originalReadsCount = getRolesReadCount();
+
+ // enure roles are cached
+ assertThat(roleA.canLogin()).isTrue();
+ assertThat(roleB.canLogin()).isTrue();
+ assertThat(originalReadsCount).isEqualTo(getRolesReadCount());
+
+ // invalidate both roles
+ ToolRunner.ToolResult tool =
ToolRunner.invokeNodetool("invalidaterolescache");
+ tool.assertOnCleanExit();
+ assertThat(tool.getStdout()).isEmpty();
+
+ // ensure role for roleA is reloaded
+ assertThat(roleA.canLogin()).isTrue();
+ long readsCountAfterFirstReLoad = getRolesReadCount();
+ assertThat(originalReadsCount).isLessThan(readsCountAfterFirstReLoad);
+
+ // ensure role for roleB is reloaded
+ assertThat(roleB.canLogin()).isTrue();
+ long readsCountAfterSecondReLoad = getRolesReadCount();
+
assertThat(readsCountAfterFirstReLoad).isLessThan(readsCountAfterSecondReLoad);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]