cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alek...@apache.org
Subject cassandra git commit: Automatically grant permissions to creators of new objects & roles
Date Fri, 13 Feb 2015 22:57:48 GMT
Repository: cassandra
Updated Branches:
  refs/heads/trunk e58a40e52 -> 9072757b5


Automatically grant permissions to creators of new objects & roles

patch by Sam Tunnicliffe; reviewed by Aleksey Yeschenko for
CASSANDRA-7216


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9072757b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9072757b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9072757b

Branch: refs/heads/trunk
Commit: 9072757b52d9a6c443871c5fba443c27606b8e57
Parents: e58a40e
Author: Sam Tunnicliffe <sam@beobal.com>
Authored: Fri Feb 6 10:32:21 2015 +0000
Committer: Aleksey Yeschenko <aleksey@apache.org>
Committed: Sat Feb 14 01:57:17 2015 +0300

----------------------------------------------------------------------
 CHANGES.txt                                     |  2 +-
 NEWS.txt                                        |  3 ++
 .../cassandra/auth/AllowAllAuthorizer.java      | 13 +++-----
 .../cassandra/auth/AuthenticatedUser.java       | 15 +++++++--
 .../org/apache/cassandra/auth/IAuthorizer.java  | 15 +++++++++
 .../statements/CreateKeyspaceStatement.java     | 28 ++++++++++------
 .../cql3/statements/CreateRoleStatement.java    | 33 ++++++++++++++++++-
 .../cql3/statements/CreateTableStatement.java   | 34 ++++++++++++++------
 .../statements/ListPermissionsStatement.java    |  9 +++++-
 .../statements/SchemaAlteringStatement.java     | 34 +++++++++++++++++++-
 10 files changed, 152 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e1ae69b..70166c1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,5 @@
 3.0
- * Add role based access control (CASSANDRA-7653, 8650)
+ * Add role based access control (CASSANDRA-7653, 8650, 7216)
  * 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/9072757b/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 801cea7..fc5b514 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -30,6 +30,9 @@ New features
      subject of permissions. Users (roles) can now be granted permissions on other
      roles, including CREATE, ALTER, DROP & AUTHORIZE, which removesthe need for
      superuser privileges in order to perform user/role management operations.
+   - Creators of database resources (Keyspaces, Tables, Roles) are now automatically
+     granted all permissions on them (if the IAuthorizer implementation supports
+     this).
    - SSTable file name is changed. Now you don't have Keyspace/CF name
      in file name. Also, secondary index has its own directory under parent's
      directory.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/auth/AllowAllAuthorizer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/AllowAllAuthorizer.java b/src/java/org/apache/cassandra/auth/AllowAllAuthorizer.java
index 7a60a14..bc6fee4 100644
--- a/src/java/org/apache/cassandra/auth/AllowAllAuthorizer.java
+++ b/src/java/org/apache/cassandra/auth/AllowAllAuthorizer.java
@@ -20,25 +20,21 @@ package org.apache.cassandra.auth;
 import java.util.Collections;
 import java.util.Set;
 
-import org.apache.cassandra.exceptions.InvalidRequestException;
-
 public class AllowAllAuthorizer implements IAuthorizer
 {
     public Set<Permission> authorize(AuthenticatedUser user, IResource resource)
     {
-        return Permission.ALL;
+        return resource.applicablePermissions();
     }
 
     public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource
resource, RoleResource to)
-    throws InvalidRequestException
     {
-        throw new InvalidRequestException("GRANT operation is not supported by AllowAllAuthorizer");
+        throw new UnsupportedOperationException("GRANT operation is not supported by AllowAllAuthorizer");
     }
 
     public void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource
resource, RoleResource from)
-    throws InvalidRequestException
     {
-        throw new InvalidRequestException("REVOKE operation is not supported by AllowAllAuthorizer");
+        throw new UnsupportedOperationException("REVOKE operation is not supported by AllowAllAuthorizer");
     }
 
     public void revokeAllFrom(RoleResource droppedRole)
@@ -50,9 +46,8 @@ public class AllowAllAuthorizer implements IAuthorizer
     }
 
     public Set<PermissionDetails> list(AuthenticatedUser performer, Set<Permission>
permissions, IResource resource, RoleResource of)
-    throws InvalidRequestException
     {
-        throw new InvalidRequestException("LIST PERMISSIONS operation is not supported by
AllowAllAuthorizer");
+        throw new UnsupportedOperationException("LIST PERMISSIONS operation is not supported
by AllowAllAuthorizer");
     }
 
     public Set<IResource> protectedResources()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/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 25d2ed4..e4a065d 100644
--- a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
+++ b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java
@@ -27,8 +27,6 @@ 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.ScheduledExecutors;
 import org.apache.cassandra.config.DatabaseDescriptor;
@@ -43,7 +41,8 @@ import org.apache.cassandra.exceptions.RequestValidationException;
  */
 public class AuthenticatedUser
 {
-    private static final Logger logger = LoggerFactory.getLogger(AuthenticatedUser.class);
+    public static final String SYSTEM_USERNAME = "system";
+    public static final AuthenticatedUser SYSTEM_USER = new AuthenticatedUser(SYSTEM_USERNAME);
 
     public static final String ANONYMOUS_USERNAME = "anonymous";
     public static final AuthenticatedUser ANONYMOUS_USER = new AuthenticatedUser(ANONYMOUS_USERNAME);
@@ -106,6 +105,16 @@ public class AuthenticatedUser
     }
 
     /**
+     * Some internal operations are performed on behalf of Cassandra itself, in those cases
+     * the system user should be used where an identity is required
+     * see CreateRoleStatement#execute() and overrides of SchemaAlteringStatement#grantPermissionsToCreator()
+     */
+    public boolean isSystem()
+    {
+        return this == SYSTEM_USER;
+    }
+
+    /**
      * Get the roles that have been granted to the user via the IRoleManager
      *
      * @return a list of roles that have been granted to the user

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/auth/IAuthorizer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/IAuthorizer.java b/src/java/org/apache/cassandra/auth/IAuthorizer.java
index c3e98e2..21b64dc 100644
--- a/src/java/org/apache/cassandra/auth/IAuthorizer.java
+++ b/src/java/org/apache/cassandra/auth/IAuthorizer.java
@@ -45,6 +45,8 @@ public interface IAuthorizer
     /**
      * Grants a set of permissions on a resource to a role.
      * The opposite of revoke().
+     * This method is optional and may be called internally, so implementations which do
+     * not support it should be sure to throw UnsupportedOperationException.
      *
      * @param performer User who grants the permissions.
      * @param permissions Set of permissions to grant.
@@ -53,6 +55,7 @@ public interface IAuthorizer
      *
      * @throws RequestValidationException
      * @throws RequestExecutionException
+     * @throws java.lang.UnsupportedOperationException
      */
     void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource
resource, RoleResource grantee)
     throws RequestValidationException, RequestExecutionException;
@@ -60,6 +63,8 @@ public interface IAuthorizer
     /**
      * Revokes a set of permissions on a resource from a user.
      * The opposite of grant().
+     * This method is optional and may be called internally, so implementations which do
+     * not support it should be sure to throw UnsupportedOperationException.
      *
      * @param performer User who revokes the permissions.
      * @param permissions Set of permissions to revoke.
@@ -68,12 +73,15 @@ public interface IAuthorizer
      *
      * @throws RequestValidationException
      * @throws RequestExecutionException
+     * @throws java.lang.UnsupportedOperationException
      */
     void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource
resource, RoleResource revokee)
     throws RequestValidationException, RequestExecutionException;
 
     /**
      * Returns a list of permissions on a resource granted to a role.
+     * This method is optional and may be called internally, so implementations which do
+     * not support it should be sure to throw UnsupportedOperationException.
      *
      * @param performer User who wants to see the permissions.
      * @param permissions Set of Permission values the user is interested in. The result
should only include the
@@ -87,6 +95,7 @@ public interface IAuthorizer
      *
      * @throws RequestValidationException
      * @throws RequestExecutionException
+     * @throws java.lang.UnsupportedOperationException
      */
     Set<PermissionDetails> list(AuthenticatedUser performer, Set<Permission>
permissions, IResource resource, RoleResource grantee)
     throws RequestValidationException, RequestExecutionException;
@@ -95,16 +104,22 @@ public interface IAuthorizer
      * Called before deleting a role with DROP ROLE statement (or the alias provided for
compatibility,
      * DROP USER) so that a new role with the same name wouldn't inherit permissions of the
deleted one in the future.
      * This removes all permissions granted to the Role in question.
+     * This method is optional and may be called internally, so implementations which do
+     * not support it should be sure to throw UnsupportedOperationException.
      *
      * @param revokee The role to revoke all permissions from.
+     * @throws java.lang.UnsupportedOperationException
      */
     void revokeAllFrom(RoleResource revokee);
 
     /**
      * This method is called after a resource is removed (i.e. keyspace, table or role is
dropped) and revokes all
      * permissions granted on the IResource in question.
+     * This method is optional and may be called internally, so implementations which do
+     * not support it should be sure to throw UnsupportedOperationException.
      *
      * @param droppedResource The resource to revoke all permissions on.
+     * @throws java.lang.UnsupportedOperationException
      */
     void revokeAllOn(IResource droppedResource);
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
index 8281cbd..2dc9c44 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
@@ -17,18 +17,12 @@
  */
 package org.apache.cassandra.cql3.statements;
 
-import org.apache.cassandra.exceptions.ConfigurationException;
-import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.auth.*;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.exceptions.AlreadyExistsException;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.exceptions.RequestValidationException;
-import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.exceptions.*;
 import org.apache.cassandra.locator.AbstractReplicationStrategy;
-import org.apache.cassandra.service.ClientState;
-import org.apache.cassandra.service.MigrationManager;
-import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.service.*;
 import org.apache.cassandra.thrift.ThriftValidation;
 import org.apache.cassandra.transport.Event;
 
@@ -116,4 +110,20 @@ public class CreateKeyspaceStatement extends SchemaAlteringStatement
     {
         return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, keyspace());
     }
+
+    protected void grantPermissionsToCreator(QueryState state)
+    {
+        try
+        {
+            DataResource resource = DataResource.keyspace(keyspace());
+            DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER,
+                                                     resource.applicablePermissions(),
+                                                     resource,
+                                                     RoleResource.role(state.getClientState().getUser().getName()));
+        }
+        catch (RequestExecutionException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java
index 65d588b..347d20a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java
@@ -17,8 +17,10 @@
  */
 package org.apache.cassandra.cql3.statements;
 
-import org.apache.cassandra.auth.*;
+import org.apache.cassandra.auth.AuthenticatedUser;
 import org.apache.cassandra.auth.IRoleManager.Option;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.auth.RoleResource;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.RoleName;
 import org.apache.cassandra.cql3.RoleOptions;
@@ -76,6 +78,35 @@ public class CreateRoleStatement extends AuthenticationStatement
             return null;
 
         DatabaseDescriptor.getRoleManager().createRole(state.getUser(), role, opts.getOptions());
+        grantPermissionsToCreator(state);
         return null;
     }
+
+    /**
+     * Grant all applicable permissions on the newly created role to the user performing
the request
+     * see also: SchemaAlteringStatement#grantPermissionsToCreator and the overridden implementations
+     * of it in subclasses CreateKeyspaceStatement & CreateTableStatement.
+     * @param state
+     */
+    private void grantPermissionsToCreator(ClientState state)
+    {
+        // The creator of a Role automatically gets ALTER/DROP/AUTHORIZE permissions on it
if:
+        // * the user is not anonymous
+        // * the configured IAuthorizer supports granting of permissions (not all do, AllowAllAuthorizer
doesn't and
+        //   custom external implementations may not)
+        if (!state.getUser().isAnonymous())
+        {
+            try
+            {
+                DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER,
+                                                         role.applicablePermissions(),
+                                                         role,
+                                                         RoleResource.role(state.getUser().getName()));
+            }
+            catch (UnsupportedOperationException e)
+            {
+                // not a problem, grant is an optional method on IAuthorizer
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
index 4ec656b..3be20a6 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
@@ -20,23 +20,23 @@ package org.apache.cassandra.cql3.statements;
 import java.nio.ByteBuffer;
 import java.util.*;
 
-import org.apache.cassandra.exceptions.*;
-import org.apache.commons.lang3.StringUtils;
 import com.google.common.collect.HashMultiset;
 import com.google.common.collect.Multiset;
+import org.apache.commons.lang3.StringUtils;
 
-import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.cql3.*;
-import org.apache.cassandra.db.composites.*;
+import org.apache.cassandra.auth.*;
+import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.db.ColumnFamilyType;
+import org.apache.cassandra.db.composites.*;
 import org.apache.cassandra.db.marshal.*;
-import org.apache.cassandra.exceptions.AlreadyExistsException;
+import org.apache.cassandra.exceptions.*;
 import org.apache.cassandra.io.compress.CompressionParameters;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
+import org.apache.cassandra.service.QueryState;
 import org.apache.cassandra.transport.Event;
 import org.apache.cassandra.utils.ByteBufferUtil;
 
@@ -119,6 +119,22 @@ public class CreateTableStatement extends SchemaAlteringStatement
         return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, Event.SchemaChange.Target.TABLE,
keyspace(), columnFamily());
     }
 
+    protected void grantPermissionsToCreator(QueryState state)
+    {
+        try
+        {
+            IResource resource = DataResource.table(keyspace(), columnFamily());
+            DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER,
+                                                     resource.applicablePermissions(),
+                                                     resource,
+                                                     RoleResource.role(state.getClientState().getUser().getName()));
+        }
+        catch (RequestExecutionException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Returns a CFMetaData instance based on the parameters parsed from this
      * <code>CREATE</code> statement, or defaults where applicable.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/cql3/statements/ListPermissionsStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ListPermissionsStatement.java b/src/java/org/apache/cassandra/cql3/statements/ListPermissionsStatement.java
index 375d160..58f8e9c 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ListPermissionsStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ListPermissionsStatement.java
@@ -102,7 +102,14 @@ public class ListPermissionsStatement extends AuthorizationStatement
     private Set<PermissionDetails> list(ClientState state, IResource resource)
     throws RequestValidationException, RequestExecutionException
     {
-        return DatabaseDescriptor.getAuthorizer().list(state.getUser(), permissions, resource,
grantee);
+        try
+        {
+            return DatabaseDescriptor.getAuthorizer().list(state.getUser(), permissions,
resource, grantee);
+        }
+        catch (UnsupportedOperationException e)
+        {
+            throw new InvalidRequestException(e.getMessage());
+        }
     }
 
     private ResultMessage resultMessage(List<PermissionDetails> details)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9072757b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
index b0d67ac..a477df6 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
@@ -17,10 +17,12 @@
  */
 package org.apache.cassandra.cql3.statements;
 
+import org.apache.cassandra.auth.AuthenticatedUser;
 import org.apache.cassandra.cql3.CFName;
 import org.apache.cassandra.cql3.CQLStatement;
 import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.RequestValidationException;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.QueryState;
 import org.apache.cassandra.transport.Event;
@@ -66,6 +68,17 @@ public abstract class SchemaAlteringStatement extends CFStatement implements
CQL
     public abstract Event.SchemaChange changeEvent();
 
     /**
+     * Schema alteration may result in a new database object (keyspace, table, role, function)
being created capable of
+     * having permissions GRANTed on it. The creator of the object (the primary role assigned
to the AuthenticatedUser
+     * performing the operation) is automatically granted ALL applicable permissions on the
object. This is a hook for
+     * subclasses to override in order to perform that grant when the statement is executed.
+     */
+    protected void grantPermissionsToCreator(QueryState state)
+    {
+        // no-op by default
+    }
+
+    /**
      * Announces the migration to other nodes in the cluster.
      * @return true if the execution of this statement resulted in a schema change, false
otherwise (when IF NOT EXISTS
      * is used, for example)
@@ -82,6 +95,25 @@ public abstract class SchemaAlteringStatement extends CFStatement implements
CQL
             return new ResultMessage.Void();
 
         Event.SchemaChange ce = changeEvent();
+
+        // when a schema alteration results in a new db object being created, we grant permissions
on the new
+        // object to the user performing the request if:
+        // * the user is not anonymous
+        // * the configured IAuthorizer supports granting of permissions (not all do, AllowAllAuthorizer
doesn't and
+        //   custom external implementations may not)
+        AuthenticatedUser user = state.getClientState().getUser();
+        if (user != null && !user.isAnonymous() && ce != null &&
ce.change == Event.SchemaChange.Change.CREATED)
+        {
+            try
+            {
+                grantPermissionsToCreator(state);
+            }
+            catch (UnsupportedOperationException e)
+            {
+                // not a problem, grant is an optional method on IAuthorizer
+            }
+        }
+
         return ce == null ? new ResultMessage.Void() : new ResultMessage.SchemaChange(ce);
     }
 


Mime
View raw message