From 759cbb0e61109c15037a5fd8c9a4105fdcbdd1cb Mon Sep 17 00:00:00 2001 From: Coleman Jackson Date: Sun, 26 Mar 2017 17:29:02 -0500 Subject: [PATCH 1/2] Started making changes to the codebase. --- conf/cassandra.yaml | 10 ++++-- src/antlr/Lexer.g | 2 ++ src/antlr/Parser.g | 2 ++ .../org/apache/cassandra/auth/AbacProxy.java | 34 +++++++++++++++++++ .../apache/cassandra/auth/AuthKeyspace.java | 18 +++++++++- .../apache/cassandra/auth/RoleResource.java | 2 ++ .../org/apache/cassandra/config/Config.java | 1 + .../cassandra/config/DatabaseDescriptor.java | 5 +++ .../apache/cassandra/cql3/CQLStatement.java | 6 ++++ .../apache/cassandra/cql3/QueryProcessor.java | 12 +++++++ .../statements/AuthenticationStatement.java | 12 +++++++ .../statements/AuthorizationStatement.java | 12 +++++++ .../cql3/statements/BatchStatement.java | 14 ++++++++ .../statements/ModificationStatement.java | 28 ++++++++++++--- .../statements/SchemaAlteringStatement.java | 12 +++++++ .../cql3/statements/SelectStatement.java | 21 ++++++++++++ .../cql3/statements/TruncateStatement.java | 10 ++++++ .../cql3/statements/UseStatement.java | 12 +++++++ .../apache/cassandra/service/ClientState.java | 16 +++++++++ 19 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 src/java/org/apache/cassandra/auth/AbacProxy.java diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index d8392a065583..ebb58396f7a7 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -7,7 +7,7 @@ # The name of the cluster. This is mainly used to prevent machines in # one logical cluster from joining another. -cluster_name: 'Test Cluster' +cluster_name: 'ABAC TEST' # This defines the number of tokens randomly assigned to this node on the ring # The more tokens, relative to other nodes, the larger the proportion of data @@ -100,7 +100,7 @@ batchlog_replay_throttle_in_kb: 1024 # users. It keeps usernames and hashed passwords in system_auth.roles table. # Please increase system_auth keyspace replication factor if you use this authenticator. # If using PasswordAuthenticator, CassandraRoleManager must also be used (see below) -authenticator: AllowAllAuthenticator +authenticator: CassandraAuthenticator # TODO: Update these or change them back potentially. # Authorization backend, implementing IAuthorizer; used to limit access/provide permissions # Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer, @@ -109,7 +109,7 @@ authenticator: AllowAllAuthenticator # - AllowAllAuthorizer allows any action to any user - set it to disable authorization. # - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please # increase system_auth keyspace replication factor if you use this authorizer. -authorizer: AllowAllAuthorizer +authorizer: CassandraAuthorizer # TODO: Update these or change them back potentially. # Part of the Authentication & Authorization backend, implementing IRoleManager; used # to maintain grants and memberships between roles. @@ -122,6 +122,10 @@ authorizer: AllowAllAuthorizer # increase system_auth keyspace replication factor if you use this role manager. role_manager: CassandraRoleManager +# Choose to use or disregard ABAC configuration. This will work regardless what other +#security options are enabled. +use_abac: true + # Validity period for roles cache (fetching granted roles can be an expensive # operation depending on the role manager, CassandraRoleManager is one example) # Granted roles are cached for authenticated sessions in AuthenticatedUser and diff --git a/src/antlr/Lexer.g b/src/antlr/Lexer.g index 168596437a78..9b40cb4c941b 100644 --- a/src/antlr/Lexer.g +++ b/src/antlr/Lexer.g @@ -139,6 +139,8 @@ K_USER: U S E R; K_USERS: U S E R S; K_ROLE: R O L E; K_ROLES: R O L E S; +K_ATTRIBUTE A T T R I B U T E; +K_POLICY P O L I C Y; K_SUPERUSER: S U P E R U S E R; K_NOSUPERUSER: N O S U P E R U S E R; K_PASSWORD: P A S S W O R D; diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g index 6cc5f1b1ba11..2734a906f8e9 100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@ -1808,5 +1808,7 @@ basic_unreserved_keyword returns [String str] | K_PER | K_PARTITION | K_GROUP + | K_ATTRIBUTE + | K_POLICY ) { $str = $k.text; } ; diff --git a/src/java/org/apache/cassandra/auth/AbacProxy.java b/src/java/org/apache/cassandra/auth/AbacProxy.java new file mode 100644 index 000000000000..c575677d2794 --- /dev/null +++ b/src/java/org/apache/cassandra/auth/AbacProxy.java @@ -0,0 +1,34 @@ +package org.apache.cassandra.auth; + +/** + * Created by coleman on 3/26/17. + */ +public final class AbacProxy +{ + // TODO: Add ABAC management methods... + + public static void addPolicy() + { + + } + + public static void dropPolicy() + { + + } + + public static void alterPolicy() + { + + } + + public static void listAllPolicies() + { + + } + + public static void listAllPoliciesOn() + { + + } +} diff --git a/src/java/org/apache/cassandra/auth/AuthKeyspace.java b/src/java/org/apache/cassandra/auth/AuthKeyspace.java index a7079dc6ae74..1799438d7db7 100644 --- a/src/java/org/apache/cassandra/auth/AuthKeyspace.java +++ b/src/java/org/apache/cassandra/auth/AuthKeyspace.java @@ -40,8 +40,23 @@ private AuthKeyspace() public static final String ROLE_PERMISSIONS = "role_permissions"; public static final String RESOURCE_ROLE_INDEX = "resource_role_permissons_index"; + public static final String POLICIES = "policies"; + public static final long SUPERUSER_SETUP_DELAY = Long.getLong("cassandra.superuser_setup_delay_ms", 10000); + private static final TableMetadata Policies = + parse(POLICIES, + "abac policy definitions", + "CREATE TABLE %s (" + + "policy text," + + "description text," + + "columnfamily text," + + "column text," + + "attribute text," + + "relation text," + + "type text," + + "PRIMARY KEY(policy, columnfamily))"); + private static final TableMetadata Roles = parse(ROLES, "role definitions", @@ -51,6 +66,7 @@ private AuthKeyspace() + "can_login boolean," + "salted_hash text," + "member_of set," + + "attributes map," + "PRIMARY KEY(role))"); private static final TableMetadata RoleMembers = @@ -93,6 +109,6 @@ public static KeyspaceMetadata metadata() { return KeyspaceMetadata.create(SchemaConstants.AUTH_KEYSPACE_NAME, KeyspaceParams.simple(1), - Tables.of(Roles, RoleMembers, RolePermissions, ResourceRoleIndex)); + Tables.of(Policies, Roles, RoleMembers, RolePermissions, ResourceRoleIndex)); } } diff --git a/src/java/org/apache/cassandra/auth/RoleResource.java b/src/java/org/apache/cassandra/auth/RoleResource.java index bf3b0ecd4eb1..7f9b2585c0c9 100644 --- a/src/java/org/apache/cassandra/auth/RoleResource.java +++ b/src/java/org/apache/cassandra/auth/RoleResource.java @@ -17,6 +17,8 @@ */ package org.apache.cassandra.auth; +import java.util.Arrays; +import java.util.List; import java.util.Set; import com.google.common.base.Objects; diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index 36ce57692e51..716174d5a1bd 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -50,6 +50,7 @@ public class Config public String authenticator; public String authorizer; public String role_manager; + public volatile boolean use_abac = true; public volatile int permissions_validity_in_ms = 2000; public volatile int permissions_cache_max_entries = 1000; public volatile int permissions_update_interval_in_ms = -1; diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index 4fb742c5a59d..dd6794f5fe16 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -2266,4 +2266,9 @@ public static BackPressureStrategy getBackPressureStrategy() { return backPressureStrategy; } + + public static boolean isUsingAbac() + { + return conf.use_abac; + } } diff --git a/src/java/org/apache/cassandra/cql3/CQLStatement.java b/src/java/org/apache/cassandra/cql3/CQLStatement.java index 901ecd4b48d7..6f7f5dfe0bda 100644 --- a/src/java/org/apache/cassandra/cql3/CQLStatement.java +++ b/src/java/org/apache/cassandra/cql3/CQLStatement.java @@ -67,4 +67,10 @@ public interface CQLStatement * @return functions all functions found (may contain duplicates) */ public Iterable getFunctions(); + + /** + * Return the cql query string that represents this query after ABAC is considered. + * @return string representing the ABAC decorated query. + */ + public String decorateAbac(ClientState clientState, String cqlQuery); } diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java index 4aa2026cdfaa..dcc7eeda932b 100644 --- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java @@ -225,6 +225,18 @@ public ResultMessage process(String queryString, QueryState queryState, QueryOpt if (prepared.getBoundTerms() != options.getValues().size()) throw new InvalidRequestException("Invalid amount of bind variables"); + if (DatabaseDescriptor.isUsingAbac()) + { + ClientState clientState = queryState.getClientState(); + + String decoratedCqlQuery = prepared.decorateAbac(clientState, ); + + if(!decoratedCqlQuery.equals(queryString) || null == decoratedCqlQuery) + { + return process(decoratedCqlQuery, queryState, options, queryStartNanoTime); + } + } + if (!queryState.getClientState().isInternal) metrics.regularStatementsExecuted.inc(); diff --git a/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java b/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java index 0283009da576..f9f6c8ee1089 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java @@ -69,5 +69,17 @@ public void checkPermission(ClientState state, Permission required, RoleResource state.getUser().getName())); } } + + /** + * Not required for Authentication statement. + * @param clientState + * @param cqlQuery + * @return + */ + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + return null; + } } diff --git a/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java b/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java index 83081c80df2e..14f99ddd7b4c 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java @@ -66,4 +66,16 @@ public static IResource maybeCorrectResource(IResource resource, ClientState sta } return resource; } + + /** + * Not required for Authorization statements. + * @param clientState + * @param cqlQuery + * @return + */ + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + return null; + } } diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java index e1819683e471..1a9eb6a99034 100644 --- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java @@ -130,6 +130,20 @@ public Iterable getFunctions() return functions; } + /** + * Batches are not yet supported by ABAC. + * @param clientState + * @param cqlQuery + * @return + */ + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + // TODO: ABAC, NOT SURE HOW TO DO BATCHES YET... + + return null; + } + public int getBoundTerms() { return boundTerms; diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index e82d840a4b63..3a07b9992e35 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -21,15 +21,12 @@ import java.util.*; import com.google.common.collect.Iterables; +import org.apache.cassandra.schema.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.schema.ColumnMetadata; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.ColumnMetadata.Raw; -import org.apache.cassandra.schema.ViewMetadata; import org.apache.cassandra.cql3.*; import org.apache.cassandra.cql3.conditions.ColumnCondition; import org.apache.cassandra.cql3.conditions.ColumnConditions; @@ -907,4 +904,27 @@ protected static ColumnMetadata getColumnDefinition(TableMetadata metadata, Raw return rawId.prepare(metadata); } } + + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + if(metadata.isView()) + { + int c; + + TableMetadataRef tableMetadataRef = View.findBaseTable(keyspace(), columnFamily()); + if(tableMetadataRef != null) + { + return clientState.decorateAbac(tableMetadataRef, cqlQuery); + } + else + { + return null; + } + } + else + { + return clientState.decorateAbac(metadata, cqlQuery); + } + } } diff --git a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java index 4a20451fb5a4..01426af4cc3d 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java @@ -120,4 +120,16 @@ public ResultMessage executeInternal(QueryState state, QueryOptions options) Event.SchemaChange ce = announceMigration(state, true); return ce == null ? new ResultMessage.Void() : new ResultMessage.SchemaChange(ce); } + + /** + * Not required for Schema Altering statements. + * @param clientState + * @param cqlQuery + * @return + */ + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + return null; + } } diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index 652b549b79b8..5af7ff71f6e8 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -1306,4 +1306,25 @@ public int compare(List a, List b) return 0; } } + + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + if(table.isView()) + { + TableMetadataRef tableMetadataRef = View.findBaseTable(keyspace(), columnFamily()); + if(tableMetadataRef != null) + { + return clientState.decorateAbac(tableMetadataRef, cqlQuery); + } + else + { + return null; + } + } + else + { + return clientState.decorateAbac(table, cqlQuery); + } + } } diff --git a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java index 300d8f44a4d4..1cfe00f7364c 100644 --- a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java @@ -88,4 +88,14 @@ public ResultMessage executeInternal(QueryState state, QueryOptions options) } return null; } + + /** + * Doesn't require any ABAC processing because truncate statements are not + * eligible for ABAC. + */ + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + return null; + } } diff --git a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java index 02a678a794bb..aaeead1459f2 100644 --- a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java @@ -65,4 +65,16 @@ public ResultMessage executeInternal(QueryState state, QueryOptions options) thr // but for some unit tests we need to set the keyspace (e.g. for tests with DROP INDEX) return execute(state, options, System.nanoTime()); } + + /** + * Not required for a Use statement, since these statments are not eligible for ABAC. + * @param clientState + * @param cqlQuery + * @return + */ + @Override + public String decorateAbac(ClientState clientState, String cqlQuery) + { + return null; + } } diff --git a/src/java/org/apache/cassandra/service/ClientState.java b/src/java/org/apache/cassandra/service/ClientState.java index 80cf810607d8..dddaf17f6f86 100644 --- a/src/java/org/apache/cassandra/service/ClientState.java +++ b/src/java/org/apache/cassandra/service/ClientState.java @@ -45,6 +45,8 @@ import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.CassandraVersion; +import javax.xml.crypto.Data; + /** * State related to a client connection. */ @@ -424,4 +426,18 @@ private Set authorize(IResource resource) { return user.getPermissions(resource); } + + public String decorateAbac(TableMetadata table, String cqlQuery) + { + // TODO: Decorate Abac + + return null; + } + + public String decorateAbac(TableMetadataRef tableMetadataRef, String cqlQuery) + { + // TODO: DecorateAbac + + return null; + } } From 827f1bb7f7cdf87deb7bac13202ed160777e4971 Mon Sep 17 00:00:00 2001 From: Coleman Jackson Date: Mon, 27 Mar 2017 15:01:07 -0500 Subject: [PATCH 2/2] Made a bunch of changes, tickets 5-10 --- build.properties.default | 4 - build.xml | 1 - src/antlr/Lexer.g | 3 + src/antlr/Parser.g | 93 +++++++++++++++++++ .../org/apache/cassandra/auth/AbacProxy.java | 11 +-- .../apache/cassandra/auth/AuthKeyspace.java | 5 +- .../cassandra/auth/AuthenticatedUser.java | 18 ++++ .../cassandra/auth/CassandraRoleManager.java | 35 ++++++- .../org/apache/cassandra/auth/IResource.java | 8 ++ .../apache/cassandra/auth/IRoleManager.java | 12 ++- .../org/apache/cassandra/auth/Permission.java | 3 + .../apache/cassandra/auth/PolicyCache.java | 41 ++++++++ .../apache/cassandra/auth/RoleOptions.java | 31 +++++++ .../apache/cassandra/auth/RoleResource.java | 6 ++ .../cassandra/config/DatabaseDescriptor.java | 14 ++- .../apache/cassandra/cql3/PolicyClause.java | 60 ++++++++++++ .../org/apache/cassandra/cql3/PolicyName.java | 40 ++++++++ .../cql3/statements/AbacStatement.java | 72 ++++++++++++++ .../cql3/statements/AlterPolicyStatement.java | 70 ++++++++++++++ .../statements/CreatePolicyStatement.java | 68 ++++++++++++++ .../cql3/statements/DropPolicyStatement.java | 61 ++++++++++++ .../statements/ListPoliciesStatement.java | 55 +++++++++++ 22 files changed, 686 insertions(+), 25 deletions(-) delete mode 100644 build.properties.default create mode 100644 src/java/org/apache/cassandra/auth/PolicyCache.java create mode 100644 src/java/org/apache/cassandra/cql3/PolicyClause.java create mode 100644 src/java/org/apache/cassandra/cql3/PolicyName.java create mode 100644 src/java/org/apache/cassandra/cql3/statements/AbacStatement.java create mode 100644 src/java/org/apache/cassandra/cql3/statements/AlterPolicyStatement.java create mode 100644 src/java/org/apache/cassandra/cql3/statements/CreatePolicyStatement.java create mode 100644 src/java/org/apache/cassandra/cql3/statements/DropPolicyStatement.java create mode 100644 src/java/org/apache/cassandra/cql3/statements/ListPoliciesStatement.java diff --git a/build.properties.default b/build.properties.default deleted file mode 100644 index 45d65d9388ef..000000000000 --- a/build.properties.default +++ /dev/null @@ -1,4 +0,0 @@ -# Maven2 Repository Locations (you can override these in "build.properties" to point to a local proxy, e.g. Nexus) -artifact.remoteRepository.central: http://repo1.maven.org/maven2 -artifact.remoteRepository.apache: https://repository.apache.org/content/repositories/releases - diff --git a/build.xml b/build.xml index 73d3051998f8..f4d77b8d4136 100644 --- a/build.xml +++ b/build.xml @@ -21,7 +21,6 @@ xmlns:artifact="antlib:org.apache.maven.artifact.ant"> - diff --git a/src/antlr/Lexer.g b/src/antlr/Lexer.g index 9b40cb4c941b..0ddc7753eea0 100644 --- a/src/antlr/Lexer.g +++ b/src/antlr/Lexer.g @@ -140,7 +140,10 @@ K_USERS: U S E R S; K_ROLE: R O L E; K_ROLES: R O L E S; K_ATTRIBUTE A T T R I B U T E; +K_ATTRIBUTES A T T R I B U T E S; K_POLICY P O L I C Y; +K_POLICIES P O L I C I E S; +K_DENY D E N Y; K_SUPERUSER: S U P E R U S E R; K_NOSUPERUSER: N O S U P E R U S E R; K_PASSWORD: P A S S W O R D; diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g index 2734a906f8e9..05e135062819 100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@ -243,6 +243,10 @@ cqlStatement returns [ParsedStatement stmt] | st38=createMaterializedViewStatement { $stmt = st38; } | st39=dropMaterializedViewStatement { $stmt = st39; } | st40=alterMaterializedViewStatement { $stmt = st40; } + | st41=createPolicyStatement { $stmt = st41; } + | st42=dropPolicyStatement { $stmt = st42; } + | st43=alterPolicyStatement { $stmt = st43; } + | st44=listPoliciesStatement { $stmt = st44; } ; /* @@ -1005,6 +1009,62 @@ truncateStatement returns [TruncateStatement stmt] : K_TRUNCATE (K_COLUMNFAMILY)? cf=columnFamilyName { $stmt = new TruncateStatement(cf); } ; +/** + * CREATE POLICY + * ON + * DENY + * IF ATTRIBUTE ; // TODO: Need to test. + */ +createPolicyStatement returns [CreatePolicyStatement stmt] + : K_CREATE K_POLICY + pn=policyName + K_ON + name=columnFamilyName + K_DENY + perms=basicPermissions + K_IF K_ATTRIBUTE + pc=policyClause + { $stmt = new CreatePolicyStatement(pn, name, perms, pc); } + ; + +/** + * DROP POLICY ON
; // TODO: Need to test. + */ +dropPolicyStatement returns [DropPolicyStatement stmt] + : K_DROP K_POLICY + pn=policyName + K_ON + name=columnFamilyName + { $stmt = new DropPolicyStatement(pn, name); } + ; + +/** + * ALTER POLICY + * ON
+ * DENY + * IF ATTRIBUTE ; // TODO: Need to test. + */ + alterPolicyStatement returns [AlterPolicyStatement stmt] + : K_ALTER K_POLICY + pn=policyName + K_ON + name=columnFamilyName + K_DENY + perms=basicPermissions + K_IF K_ATTRIBUTE + pc=policyClause + { $stmt = new CreatePolicyStatement(pn, name, perms, pc); } + ; + +/** + * LIST ALL POLICIES ON
; // TODO: Need to test. + */ +listPoliciesStatement returns [ListPoliciesStatement stmt] + : K_LIST K_ALL K_POLICIES K_ON + name=columnFamilyName + { $stmt = new ListPoliciesStatement(name); } + ; + /** * GRANT ON TO */ @@ -1067,6 +1127,14 @@ listPermissionsStatement returns [ListPermissionsStatement stmt] { $stmt = new ListPermissionsStatement($permissionOrAll.perms, resource, grantee, recursive); } ; +basicPermissions returns [Set perms] + : K_ALL ( K_PERMISSIONS )? { $perms = Permission.BASIC; } + : K_SELECT ( K_PERMISSION )? { $perms = EnumSet.of(Permission.SELECT); } + : K_MODIFY ( K_PERMISSION )? { $perms = EnumSet.of(Permission.MODIFY); } + ; + +policyClause returns [PolicyClause pc] + permission returns [Permission perm] : p=(K_CREATE | K_ALTER | K_DROP | K_SELECT | K_MODIFY | K_AUTHORIZE | K_DESCRIBE | K_EXECUTE) { $perm = Permission.valueOf($p.text.toUpperCase()); } @@ -1137,6 +1205,7 @@ createUserStatement returns [CreateRoleStatement stmt] : K_CREATE K_USER (K_IF K_NOT K_EXISTS { ifNotExists = true; })? u=username { name.setName($u.text, true); } ( K_WITH userPassword[opts] )? ( K_SUPERUSER { superuser = true; } | K_NOSUPERUSER { superuser = false; } )? + ( K_ATTRIBUTES '=' map=fullMapLiteral { opts.setOption(IRoleManager.Option.ATTRIBUTES, map); } )? { opts.setOption(IRoleManager.Option.SUPERUSER, superuser); $stmt = new CreateRoleStatement(name, opts, ifNotExists); } ; @@ -1153,6 +1222,7 @@ alterUserStatement returns [AlterRoleStatement stmt] ( K_WITH userPassword[opts] )? ( K_SUPERUSER { opts.setOption(IRoleManager.Option.SUPERUSER, true); } | K_NOSUPERUSER { opts.setOption(IRoleManager.Option.SUPERUSER, false); } ) ? + ( K_ATTRIBUTES '=' map=fullMapLiteral { opts.setOption(IRoleManager.Option.ATTRIBUTES, map); } )? { $stmt = new AlterRoleStatement(name, opts); } ; @@ -1256,6 +1326,7 @@ roleOption[RoleOptions opts] | K_OPTIONS '=' m=fullMapLiteral { opts.setOption(IRoleManager.Option.OPTIONS, convertPropertyMap(m)); } | K_SUPERUSER '=' b=BOOLEAN { opts.setOption(IRoleManager.Option.SUPERUSER, Boolean.valueOf($b.text)); } | K_LOGIN '=' b=BOOLEAN { opts.setOption(IRoleManager.Option.LOGIN, Boolean.valueOf($b.text)); } + | K_ATTRIBUTES '=' map=fullMapLiteral { opts.setOption(IRoleManager.Option.ATTRIBUTES, map); } ; // for backwards compatibility in CREATE/ALTER USER, this has no '=' @@ -1319,6 +1390,10 @@ userOrRoleName returns [RoleName name] : roleName[role] {$name = role;} ; +policyName returns [PolicyName pn] + @init { PolicyName pol = PolicyName(); } + : plcyName[pol] { $pn = pol; } + ksName[KeyspaceElementName name] : t=IDENT { $name.setKeyspace($t.text, false);} | t=QUOTED_NAME { $name.setKeyspace($t.text, true);} @@ -1348,6 +1423,21 @@ roleName[RoleName name] | QMARK {addRecognitionError("Bind variables cannot be used for role names");} ; +plcyName[PolicyName pn] + : t=IDENT { $pn.setName($t.text, false); } + | s=STRING_LITERAL { $pn.setName($s.text, true); } + | t=QUOTED_NAME { $pn.setName($t.text, true); } + | k=unreserved_keyword { $pn.setName(k, false); } + | QMARK {addRecognitionError("Bind variables cannot be used for policy names");} + ; + +policyClause returns [PolicyClause pc] // TODO: Test This + : attr=STRING_LITERAL type=relationType col=cident { $pc = new PolicyClause($attr.text, type, col); } + | attr=STRING_LITERAL K_IS K_NOT K_NULL { $pc = new PolicyClause($attr.text, Operator.IS_NOT, Constants.NULL_LITERAL); } + | attr=STRING_LITERAL K_IN col=cident { $pc = new PolicyClause($attr.text, col); } + | attr=STRING_LITERAL cont=containsOperator col=cident { $pc = new PolicyClause($attr.text, cont, col); } + ; + constant returns [Constants.Literal constant] : t=STRING_LITERAL { $constant = Constants.Literal.string($t.text); } | t=INTEGER { $constant = Constants.Literal.integer($t.text); } @@ -1809,6 +1899,9 @@ basic_unreserved_keyword returns [String str] | K_PARTITION | K_GROUP | K_ATTRIBUTE + | K_ATTRIBUTES | K_POLICY + | K_POLICIES + | K_DENY ) { $str = $k.text; } ; diff --git a/src/java/org/apache/cassandra/auth/AbacProxy.java b/src/java/org/apache/cassandra/auth/AbacProxy.java index c575677d2794..0ef6fa1d5cf2 100644 --- a/src/java/org/apache/cassandra/auth/AbacProxy.java +++ b/src/java/org/apache/cassandra/auth/AbacProxy.java @@ -1,5 +1,9 @@ package org.apache.cassandra.auth; +import org.apache.cassandra.cql3.PolicyClause; + +import java.util.Set; + /** * Created by coleman on 3/26/17. */ @@ -22,12 +26,7 @@ public static void alterPolicy() } - public static void listAllPolicies() - { - - } - - public static void listAllPoliciesOn() + static Set listAllPoliciesOn(IResource resource, Permission permission) { } diff --git a/src/java/org/apache/cassandra/auth/AuthKeyspace.java b/src/java/org/apache/cassandra/auth/AuthKeyspace.java index 1799438d7db7..7ee947906a72 100644 --- a/src/java/org/apache/cassandra/auth/AuthKeyspace.java +++ b/src/java/org/apache/cassandra/auth/AuthKeyspace.java @@ -50,10 +50,7 @@ private AuthKeyspace() "CREATE TABLE %s (" + "policy text," + "description text," - + "columnfamily text," - + "column text," - + "attribute text," - + "relation text," + + "obj blob," + "type text," + "PRIMARY KEY(policy, columnfamily))"); diff --git a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java index 5e5730805b2e..b771879ad6ef 100644 --- a/src/java/org/apache/cassandra/auth/AuthenticatedUser.java +++ b/src/java/org/apache/cassandra/auth/AuthenticatedUser.java @@ -17,6 +17,7 @@ */ package org.apache.cassandra.auth; +import java.util.HashSet; import java.util.Set; import com.google.common.base.Objects; @@ -104,6 +105,23 @@ public Set getPermissions(IResource resource) return permissionsCache.getPermissions(this, resource); } + public Set getAttribute(String attributeName) + { + Set ret = new HashSet<>(); + + for(RoleResource role : Roles.getRoles(role)) + { + Object attr = role.getAttribute(attributeName); + + if(attr != null) + { + ret.add(attr); + } + } + + return ret; + } + @Override public String toString() { diff --git a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java index 244f7869326a..eddfa8acc65d 100644 --- a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java +++ b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java @@ -17,6 +17,7 @@ */ package org.apache.cassandra.auth; +import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -25,6 +26,7 @@ import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import org.apache.cassandra.db.marshal.AbstractType; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -179,6 +181,29 @@ public void setup() } } + @Override + public Object getRoleAttribute(RoleResource roleResource, String attributeName) + { + String selectCql = String.format("SELECT attributes FROM %s.%s WHERE role = %s", + SchemaConstants.AUTH_KEYSPACE_NAME, + AuthKeyspace.ROLES, + roleResource.getName()); + + UntypedResultSet results = process(selectCql, consistencyForRole(roleResource.getRoleName())); + + if(results.isEmpty()) + { + return null; + } + + Map attributes = + results.one().getMap("attributes", + (AbstractType)CQL3Type.Native.TEXT.getType(), + (AbstractType)CQL3Type.Native.BLOB.getType()); + + return attributes.get(attributeName); + } + public Set