diff --git a/CHANGES.txt b/CHANGES.txt index db8d9875c3..4f7d4e70c9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1 + * Pre hashed passwords in CQL (CASSANDRA-17334) * Increase cqlsh version (CASSANDRA-17432) * Update SUPPORTED_UPGRADE_PATHS to include 3.0 and 3.x to 4.1 paths and remove obsolete tests (CASSANDRA-17362) * Support DELETE in CQLSSTableWriter (CASSANDRA-14797) diff --git a/NEWS.txt b/NEWS.txt index 5cad3e6ae8..cb869deb3c 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -56,6 +56,7 @@ using the provided 'sstableupgrade' tool. New features ------------ + - Support for pre hashing passwords on CQL DCL commands - Expose all client options via system_views.clients and nodetool clientstats. - Support for String concatenation has been added through the + operator. - New configuration max_hints_size_per_host to limit the size of local hints files per host in mebibytes. Setting to diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index 5a9e4981f6..762a666c71 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -1439,7 +1439,7 @@ syntax_rules += r''' ; ::= "CREATE" "USER" ( "IF" "NOT" "EXISTS" )? - ( "WITH" "PASSWORD" )? + ( "WITH" ("HASHED")? "PASSWORD" )? ( "SUPERUSER" | "NOSUPERUSER" )? ; @@ -1469,7 +1469,7 @@ syntax_rules += r''' ( "WITH" ("AND" )*)? ; - ::= "PASSWORD" "=" + ::= (("HASHED")? "PASSWORD") "=" | "OPTIONS" "=" | "SUPERUSER" "=" | "LOGIN" "=" diff --git a/src/antlr/Lexer.g b/src/antlr/Lexer.g index d89097e55d..34c7e2ed2f 100644 --- a/src/antlr/Lexer.g +++ b/src/antlr/Lexer.g @@ -150,6 +150,7 @@ K_ROLES: R O L E S; 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; +K_HASHED: H A S H E D; K_LOGIN: L O G I N; K_NOLOGIN: N O L O G I N; K_OPTIONS: O P T I O N S; diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g index efc86648ed..fd74c2a3f9 100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@ -1169,6 +1169,10 @@ createUserStatement returns [CreateRoleStatement stmt] ( K_WITH userPassword[opts] )? ( K_SUPERUSER { superuser = true; } | K_NOSUPERUSER { superuser = false; } )? { opts.setOption(IRoleManager.Option.SUPERUSER, superuser); + if (opts.getPassword().isPresent() && opts.getHashedPassword().isPresent()) + { + throw new SyntaxException("Options 'password' and 'hashed password' are mutually exclusive"); + } $stmt = new CreateRoleStatement(name, opts, DCPermissions.all(), ifNotExists); } ; @@ -1184,7 +1188,13 @@ alterUserStatement returns [AlterRoleStatement stmt] ( K_WITH userPassword[opts] )? ( K_SUPERUSER { opts.setOption(IRoleManager.Option.SUPERUSER, true); } | K_NOSUPERUSER { opts.setOption(IRoleManager.Option.SUPERUSER, false); } ) ? - { $stmt = new AlterRoleStatement(name, opts, null); } + { + if (opts.getPassword().isPresent() && opts.getHashedPassword().isPresent()) + { + throw new SyntaxException("Options 'password' and 'hashed password' are mutually exclusive"); + } + $stmt = new AlterRoleStatement(name, opts, null); + } ; /** @@ -1232,6 +1242,10 @@ createRoleStatement returns [CreateRoleStatement stmt] { opts.setOption(IRoleManager.Option.SUPERUSER, false); } + if (opts.getPassword().isPresent() && opts.getHashedPassword().isPresent()) + { + throw new SyntaxException("Options 'password' and 'hashed password' are mutually exclusive"); + } $stmt = new CreateRoleStatement(name, opts, dcperms.build(), ifNotExists); } ; @@ -1252,7 +1266,13 @@ alterRoleStatement returns [AlterRoleStatement stmt] } : K_ALTER K_ROLE name=userOrRoleName ( K_WITH roleOptions[opts, dcperms] )? - { $stmt = new AlterRoleStatement(name, opts, dcperms.isModified() ? dcperms.build() : null); } + { + if (opts.getPassword().isPresent() && opts.getHashedPassword().isPresent()) + { + throw new SyntaxException("Options 'password' and 'hashed password' are mutually exclusive"); + } + $stmt = new AlterRoleStatement(name, opts, dcperms.isModified() ? dcperms.build() : null); + } ; /** @@ -1286,6 +1306,7 @@ roleOptions[RoleOptions opts, DCPermissions.Builder dcperms] roleOption[RoleOptions opts, DCPermissions.Builder dcperms] : K_PASSWORD '=' v=STRING_LITERAL { opts.setOption(IRoleManager.Option.PASSWORD, $v.text); } + | K_HASHED K_PASSWORD '=' v=STRING_LITERAL { opts.setOption(IRoleManager.Option.HASHED_PASSWORD, $v.text); } | 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)); } @@ -1300,6 +1321,7 @@ dcPermission[DCPermissions.Builder builder] // for backwards compatibility in CREATE/ALTER USER, this has no '=' userPassword[RoleOptions opts] : K_PASSWORD v=STRING_LITERAL { opts.setOption(IRoleManager.Option.PASSWORD, $v.text); } + | K_HASHED K_PASSWORD v=STRING_LITERAL { opts.setOption(IRoleManager.Option.HASHED_PASSWORD, $v.text); } ; /** @@ -1869,6 +1891,7 @@ basic_unreserved_keyword returns [String str] | K_NOLOGIN | K_OPTIONS | K_PASSWORD + | K_HASHED | K_EXISTS | K_CUSTOM | K_TRIGGER diff --git a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java index 6e8f7d8337..0344de921d 100644 --- a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java +++ b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java @@ -135,10 +135,10 @@ public class CassandraRoleManager implements IRoleManager public CassandraRoleManager() { supportedOptions = DatabaseDescriptor.getAuthenticator() instanceof PasswordAuthenticator - ? ImmutableSet.of(Option.LOGIN, Option.SUPERUSER, Option.PASSWORD) + ? ImmutableSet.of(Option.LOGIN, Option.SUPERUSER, Option.PASSWORD, Option.HASHED_PASSWORD) : ImmutableSet.of(Option.LOGIN, Option.SUPERUSER); alterableOptions = DatabaseDescriptor.getAuthenticator() instanceof PasswordAuthenticator - ? ImmutableSet.of(Option.PASSWORD) + ? ImmutableSet.of(Option.PASSWORD, Option.HASHED_PASSWORD) : ImmutableSet.