You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2015/03/04 01:01:28 UTC
cassandra git commit: Make LIST USERS display inherited superuser
status
Repository: cassandra
Updated Branches:
refs/heads/trunk 56348ea7b -> b32ce687e
Make LIST USERS display inherited superuser status
patch by Sam Tunnicliffe; reviewed by Aleksey Yeschenko for
CASSANDRA-8849
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b32ce687
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b32ce687
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b32ce687
Branch: refs/heads/trunk
Commit: b32ce687e713de8d8535b0607c9edd9b55a7b6aa
Parents: 56348ea
Author: Sam Tunnicliffe <sa...@beobal.com>
Authored: Tue Mar 3 15:58:48 2015 -0800
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Tue Mar 3 15:58:48 2015 -0800
----------------------------------------------------------------------
CHANGES.txt | 2 +-
conf/cassandra.yaml | 8 ++
.../cassandra/auth/AuthenticatedUser.java | 89 +--------------
src/java/org/apache/cassandra/auth/Roles.java | 59 ++++++++++
.../org/apache/cassandra/auth/RolesCache.java | 109 +++++++++++++++++++
.../org/apache/cassandra/config/Config.java | 2 +
.../cassandra/config/DatabaseDescriptor.java | 12 ++
.../statements/AuthenticationStatement.java | 20 ----
.../cql3/statements/DropRoleStatement.java | 6 +-
.../cql3/statements/ListUsersStatement.java | 6 +-
.../apache/cassandra/service/ClientState.java | 2 +-
11 files changed, 200 insertions(+), 115 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index cc3658d..b877cbe 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,5 @@
3.0
- * Add role based access control (CASSANDRA-7653, 8650, 7216, 8760)
+ * Add role based access control (CASSANDRA-7653, 8650, 7216, 8760, 8849)
* Avoid accessing partitioner through StorageProxy (CASSANDRA-8244, 8268)
* Upgrade Metrics library and remove depricated metrics (CASSANDRA-5657)
* Serializing Row cache alternative, fully off heap (CASSANDRA-7438)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/conf/cassandra.yaml
----------------------------------------------------------------------
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index 3643986..240f37a 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -93,6 +93,14 @@ role_manager: CassandraRoleManager
# Will be disabled automatically for AllowAllAuthenticator.
roles_validity_in_ms: 2000
+# Refresh interval for roles cache (if enabled).
+# After this interval, cache entries become eligible for refresh. Upon next
+# access, an async reload is scheduled and the old value returned until it
+# completes. If roles_validity_in_ms is non-zero, then this must be
+# also.
+# Defaults to the same value as roles_validity_in_ms.
+# roles_update_interval_in_ms: 1000
+
# Validity period for permissions cache (fetching permissions can be an
# expensive operation depending on the authorizer, CassandraAuthorizer is
# one example). Defaults to 2000, set to 0 to disable.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
index e4a065d..ee62503 100644
--- a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
+++ b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
@@ -18,20 +18,10 @@
package org.apache.cassandra.auth;
import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
import com.google.common.base.Objects;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListenableFutureTask;
-import org.apache.cassandra.concurrent.ScheduledExecutors;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.exceptions.RequestExecutionException;
-import org.apache.cassandra.exceptions.RequestValidationException;
/**
* Returned from IAuthenticator#authenticate(), represents an authenticated user everywhere internally.
@@ -47,9 +37,6 @@ public class AuthenticatedUser
public static final String ANONYMOUS_USERNAME = "anonymous";
public static final AuthenticatedUser ANONYMOUS_USER = new AuthenticatedUser(ANONYMOUS_USERNAME);
- // User-level roles cache
- private static final LoadingCache<RoleResource, Set<RoleResource>> rolesCache = initRolesCache();
-
// User-level permissions cache.
private static final PermissionsCache permissionsCache = new PermissionsCache(DatabaseDescriptor.getPermissionsValidity(),
DatabaseDescriptor.getPermissionsUpdateInterval(),
@@ -84,16 +71,7 @@ public class AuthenticatedUser
*/
public boolean isSuper()
{
- return !isAnonymous() && hasSuperuserRole();
- }
-
- private boolean hasSuperuserRole()
- {
- IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
- for (RoleResource role : getRoles())
- if (roleManager.isSuper(role))
- return true;
- return false;
+ return !isAnonymous() && Roles.hasSuperuserStatus(role);
}
/**
@@ -121,71 +99,12 @@ public class AuthenticatedUser
*/
public Set<RoleResource> getRoles()
{
- if (rolesCache == null)
- return loadRoles(role);
-
- try
- {
- return rolesCache.get(role);
- }
- catch (Exception e)
- {
- throw new RuntimeException(e);
- }
+ return Roles.getRoles(role);
}
- public static Set<Permission> getPermissions(AuthenticatedUser user, IResource resource)
- {
- return permissionsCache.getPermissions(user, resource);
- }
-
- private static Set<RoleResource> loadRoles(RoleResource primary)
- {
- try
- {
- return DatabaseDescriptor.getRoleManager().getRoles(primary, true);
- }
- catch (RequestValidationException e)
- {
- throw new AssertionError(e); // not supposed to happen
- }
- catch (RequestExecutionException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private static LoadingCache<RoleResource, Set<RoleResource>> initRolesCache()
+ public Set<Permission> getPermissions(IResource resource)
{
- if (DatabaseDescriptor.getAuthenticator() instanceof AllowAllAuthenticator)
- return null;
-
- int validityPeriod = DatabaseDescriptor.getRolesValidity();
- if (validityPeriod <= 0)
- return null;
-
- return CacheBuilder.newBuilder()
- .refreshAfterWrite(validityPeriod, TimeUnit.MILLISECONDS)
- .build(new CacheLoader<RoleResource, Set<RoleResource>>()
- {
- public Set<RoleResource> load(RoleResource primary)
- {
- return loadRoles(primary);
- }
-
- public ListenableFuture<Set<RoleResource>> reload(final RoleResource primary, Set<RoleResource> oldValue)
- {
- ListenableFutureTask<Set<RoleResource>> task = ListenableFutureTask.create(new Callable<Set<RoleResource>>()
- {
- public Set<RoleResource> call()
- {
- return loadRoles(primary);
- }
- });
- ScheduledExecutors.optionalTasks.execute(task);
- return task;
- }
- });
+ return permissionsCache.getPermissions(this, resource);
}
@Override
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/auth/Roles.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/Roles.java b/src/java/org/apache/cassandra/auth/Roles.java
new file mode 100644
index 0000000..c73f882
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/Roles.java
@@ -0,0 +1,59 @@
+/*
+ * 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.auth;
+
+import java.util.Set;
+
+import org.apache.cassandra.config.DatabaseDescriptor;
+
+public class Roles
+{
+ private static final RolesCache cache = new RolesCache(DatabaseDescriptor.getRolesValidity(),
+ DatabaseDescriptor.getRolesUpdateInterval(),
+ DatabaseDescriptor.getRolesCacheMaxEntries(),
+ DatabaseDescriptor.getRoleManager());
+
+ /**
+ * Get all roles granted to the supplied Role, including both directly granted
+ * and inherited roles.
+ * The returned roles may be cached if roles_validity_in_ms > 0
+ *
+ * @param primaryRole the Role
+ * @return set of all granted Roles for the primary Role
+ */
+ public static Set<RoleResource> getRoles(RoleResource primaryRole)
+ {
+ return cache.getRoles(primaryRole);
+ }
+
+ /**
+ * Returns true if the supplied role or any other role granted to it
+ * (directly or indirectly) has superuser status.
+ *
+ * @param role the primary role
+ * @return true if the role has superuser status, false otherwise
+ */
+ public static boolean hasSuperuserStatus(RoleResource role)
+ {
+ IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
+ for (RoleResource r : cache.getRoles(role))
+ if (roleManager.isSuper(r))
+ return true;
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/auth/RolesCache.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/RolesCache.java b/src/java/org/apache/cassandra/auth/RolesCache.java
new file mode 100644
index 0000000..0e9e134
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/RolesCache.java
@@ -0,0 +1,109 @@
+/*
+ * 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.auth;
+
+import java.util.Set;
+import java.util.concurrent.*;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.concurrent.DebuggableThreadPoolExecutor;
+import org.apache.cassandra.config.DatabaseDescriptor;
+
+public class RolesCache
+{
+ private static final Logger logger = LoggerFactory.getLogger(RolesCache.class);
+
+ private final ThreadPoolExecutor cacheRefreshExecutor = new DebuggableThreadPoolExecutor("RolesCacheRefresh",
+ Thread.NORM_PRIORITY);
+ private final IRoleManager roleManager;
+ private final LoadingCache<RoleResource, Set<RoleResource>> cache;
+
+ public RolesCache(int validityPeriod, int updateInterval, int maxEntries, IRoleManager roleManager)
+ {
+ this.roleManager = roleManager;
+ this.cache = initCache(validityPeriod, updateInterval, maxEntries);
+ }
+
+ public Set<RoleResource> getRoles(RoleResource role)
+ {
+ if (cache == null)
+ return roleManager.getRoles(role, true);
+
+ try
+ {
+ return cache.get(role);
+ }
+ catch (ExecutionException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private LoadingCache<RoleResource, Set<RoleResource>> initCache(int validityPeriod,
+ int updateInterval,
+ int maxEntries)
+ {
+ if (DatabaseDescriptor.getAuthenticator() instanceof AllowAllAuthenticator)
+ return null;
+
+ if (validityPeriod <= 0)
+ return null;
+
+ return CacheBuilder.newBuilder()
+ .refreshAfterWrite(updateInterval, TimeUnit.MILLISECONDS)
+ .expireAfterWrite(validityPeriod, TimeUnit.MILLISECONDS)
+ .maximumSize(maxEntries)
+ .build(new CacheLoader<RoleResource, Set<RoleResource>>()
+ {
+ public Set<RoleResource> load(RoleResource primaryRole)
+ {
+ return roleManager.getRoles(primaryRole, true);
+ }
+
+ public ListenableFuture<Set<RoleResource>> reload(final RoleResource primaryRole,
+ final Set<RoleResource> oldValue)
+ {
+ ListenableFutureTask<Set<RoleResource>> task;
+ task = ListenableFutureTask.create(new Callable<Set<RoleResource>>()
+ {
+ public Set<RoleResource> call() throws Exception
+ {
+ try
+ {
+ return roleManager.getRoles(primaryRole, true);
+ }
+ catch (Exception e)
+ {
+ logger.debug("Error performing async refresh of user roles", e);
+ throw e;
+ }
+ }
+ });
+ cacheRefreshExecutor.execute(task);
+ return task;
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java
index 806c7b4..eec6826 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -48,6 +48,8 @@ public class Config
public int permissions_cache_max_entries = 1000;
public int permissions_update_interval_in_ms = -1;
public int roles_validity_in_ms = 2000;
+ public int roles_cache_max_entries = 1000;
+ public int roles_update_interval_in_ms = -1;
/* Hashing strategy Random or OPHF */
public String partitioner;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 2891e9a..92d5d5b 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -640,6 +640,18 @@ public class DatabaseDescriptor
return conf.roles_validity_in_ms;
}
+ public static int getRolesCacheMaxEntries()
+ {
+ return conf.roles_cache_max_entries;
+ }
+
+ public static int getRolesUpdateInterval()
+ {
+ return conf.roles_update_interval_in_ms == -1
+ ? conf.roles_validity_in_ms
+ : conf.roles_update_interval_in_ms;
+ }
+
public static int getThriftFramedTransportSize()
{
return conf.thrift_framed_transport_size_in_mb * 1024 * 1024;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java b/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java
index b0ea246..151e4f0 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java
@@ -17,11 +17,8 @@
*/
package org.apache.cassandra.cql3.statements;
-
-import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.auth.RoleResource;
-import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLStatement;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.exceptions.RequestExecutionException;
@@ -72,22 +69,5 @@ public abstract class AuthenticationStatement extends ParsedStatement implements
state.getUser().getName()));
}
}
-
- protected boolean hasSuperuserStatus(RoleResource role)
- {
- IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
- try
- {
- for (RoleResource r : roleManager.getRoles(role, true))
- if (roleManager.isSuper(r))
- return true;
- return false;
- }
- catch(RequestValidationException | RequestExecutionException e)
- {
- throw new RuntimeException(e);
- }
- }
-
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/cql3/statements/DropRoleStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropRoleStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropRoleStatement.java
index 44c749b..ec4bde7 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropRoleStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropRoleStatement.java
@@ -17,9 +17,7 @@
*/
package org.apache.cassandra.cql3.statements;
-import org.apache.cassandra.auth.AuthenticatedUser;
-import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.auth.RoleResource;
+import org.apache.cassandra.auth.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.RoleName;
import org.apache.cassandra.exceptions.*;
@@ -40,7 +38,7 @@ public class DropRoleStatement extends AuthenticationStatement
public void checkAccess(ClientState state) throws UnauthorizedException
{
super.checkPermission(state, Permission.DROP, role);
- if (hasSuperuserStatus(role) && !state.getUser().isSuper())
+ if (Roles.hasSuperuserStatus(role) && !state.getUser().isSuper())
throw new UnauthorizedException("Only superusers can drop a role with superuser status");
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/cql3/statements/ListUsersStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ListUsersStatement.java b/src/java/org/apache/cassandra/cql3/statements/ListUsersStatement.java
index 3edf3a4..7251980 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ListUsersStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ListUsersStatement.java
@@ -21,9 +21,7 @@ import java.util.List;
import com.google.common.collect.ImmutableList;
-import org.apache.cassandra.auth.AuthKeyspace;
-import org.apache.cassandra.auth.IRoleManager;
-import org.apache.cassandra.auth.RoleResource;
+import org.apache.cassandra.auth.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.ColumnSpecification;
@@ -53,7 +51,7 @@ public class ListUsersStatement extends ListRolesStatement
if (!roleManager.canLogin(role))
continue;
result.addColumnValue(UTF8Type.instance.decompose(role.getRoleName()));
- result.addColumnValue(BooleanType.instance.decompose(roleManager.isSuper(role)));
+ result.addColumnValue(BooleanType.instance.decompose(Roles.hasSuperuserStatus(role)));
}
return new ResultMessage.Rows(result);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b32ce687/src/java/org/apache/cassandra/service/ClientState.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/ClientState.java b/src/java/org/apache/cassandra/service/ClientState.java
index 57a57e4..1bc6e9d 100644
--- a/src/java/org/apache/cassandra/service/ClientState.java
+++ b/src/java/org/apache/cassandra/service/ClientState.java
@@ -327,6 +327,6 @@ public class ClientState
private Set<Permission> authorize(IResource resource)
{
- return AuthenticatedUser.getPermissions(user, resource);
+ return user.getPermissions(resource);
}
}