This is an automated email from the ASF dual-hosted git repository. bereng 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 2f84f49 Revert "Pre hashed passwords in CQL" 2f84f49 is described below commit 2f84f492a21ee4e6bfc581c07a5817591a783f76 Author: Bereng <berenguerbl...@gmail.com> AuthorDate: Mon Mar 21 13:19:31 2022 +0100 Revert "Pre hashed passwords in CQL" This reverts commit 6946b304e94a8a8d1250680664ddc03b61a139c9. --- CHANGES.txt | 1 - NEWS.txt | 1 - pylib/cqlshlib/cql3handling.py | 4 +- src/antlr/Lexer.g | 1 - src/antlr/Parser.g | 27 +-- .../cassandra/auth/CassandraRoleManager.java | 18 +- .../org/apache/cassandra/auth/IRoleManager.java | 2 +- .../org/apache/cassandra/auth/RoleOptions.java | 41 +---- .../apache/cassandra/cql3/PasswordObfuscator.java | 12 +- .../org/apache/cassandra/tools/HashPassword.java | 195 --------------------- .../cassandra/audit/AuditLoggerAuthTest.java | 80 +-------- .../cassandra/auth/CreateAndAlterRoleTest.java | 149 ---------------- .../org/apache/cassandra/auth/RoleOptionsTest.java | 15 +- .../cassandra/cql3/PasswordObfuscatorTest.java | 175 +++--------------- .../apache/cassandra/tools/HashPasswordTest.java | 150 ---------------- .../org/apache/cassandra/tools/ToolRunner.java | 12 +- tools/bin/hash_password | 53 ------ 17 files changed, 54 insertions(+), 882 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4f7d4e7..db8d987 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,4 @@ 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 cb869de..5cad3e6 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -56,7 +56,6 @@ 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 762a666..5a9e498 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -1439,7 +1439,7 @@ syntax_rules += r''' ; <createUserStatement> ::= "CREATE" "USER" ( "IF" "NOT" "EXISTS" )? <username> - ( "WITH" ("HASHED")? "PASSWORD" <stringLiteral> )? + ( "WITH" "PASSWORD" <stringLiteral> )? ( "SUPERUSER" | "NOSUPERUSER" )? ; @@ -1469,7 +1469,7 @@ syntax_rules += r''' ( "WITH" <roleProperty> ("AND" <roleProperty>)*)? ; -<roleProperty> ::= (("HASHED")? "PASSWORD") "=" <stringLiteral> +<roleProperty> ::= "PASSWORD" "=" <stringLiteral> | "OPTIONS" "=" <mapLiteral> | "SUPERUSER" "=" <boolean> | "LOGIN" "=" <boolean> diff --git a/src/antlr/Lexer.g b/src/antlr/Lexer.g index 34c7e2e..d89097e 100644 --- a/src/antlr/Lexer.g +++ b/src/antlr/Lexer.g @@ -150,7 +150,6 @@ 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 fd74c2a..efc8664 100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@ -1169,10 +1169,6 @@ 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); } ; @@ -1188,13 +1184,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); } ) ? - { - if (opts.getPassword().isPresent() && opts.getHashedPassword().isPresent()) - { - throw new SyntaxException("Options 'password' and 'hashed password' are mutually exclusive"); - } - $stmt = new AlterRoleStatement(name, opts, null); - } + { $stmt = new AlterRoleStatement(name, opts, null); } ; /** @@ -1242,10 +1232,6 @@ 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); } ; @@ -1266,13 +1252,7 @@ alterRoleStatement returns [AlterRoleStatement stmt] } : K_ALTER K_ROLE name=userOrRoleName ( K_WITH roleOptions[opts, dcperms] )? - { - 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); - } + { $stmt = new AlterRoleStatement(name, opts, dcperms.isModified() ? dcperms.build() : null); } ; /** @@ -1306,7 +1286,6 @@ 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)); } @@ -1321,7 +1300,6 @@ 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); } ; /** @@ -1891,7 +1869,6 @@ 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 0344de9..6e8f7d8 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, Option.HASHED_PASSWORD) + ? ImmutableSet.of(Option.LOGIN, Option.SUPERUSER, Option.PASSWORD) : ImmutableSet.of(Option.LOGIN, Option.SUPERUSER); alterableOptions = DatabaseDescriptor.getAuthenticator() instanceof PasswordAuthenticator - ? ImmutableSet.of(Option.PASSWORD, Option.HASHED_PASSWORD) + ? ImmutableSet.of(Option.PASSWORD) : ImmutableSet.<Option>of(); } @@ -172,20 +172,20 @@ public class CassandraRoleManager implements IRoleManager public void createRole(AuthenticatedUser performer, RoleResource role, RoleOptions options) throws RequestValidationException, RequestExecutionException { - String insertCql = options.getPassword().isPresent() || options.getHashedPassword().isPresent() + String insertCql = options.getPassword().isPresent() ? String.format("INSERT INTO %s.%s (role, is_superuser, can_login, salted_hash) VALUES ('%s', %s, %s, '%s')", SchemaConstants.AUTH_KEYSPACE_NAME, AuthKeyspace.ROLES, escape(role.getRoleName()), - options.getSuperuser().orElse(false), - options.getLogin().orElse(false), - options.getHashedPassword().orElseGet(() -> escape(hashpw(options.getPassword().get())))) + options.getSuperuser().or(false), + options.getLogin().or(false), + escape(hashpw(options.getPassword().get()))) : String.format("INSERT INTO %s.%s (role, is_superuser, can_login) VALUES ('%s', %s, %s)", SchemaConstants.AUTH_KEYSPACE_NAME, AuthKeyspace.ROLES, escape(role.getRoleName()), - options.getSuperuser().orElse(false), - options.getLogin().orElse(false)); + options.getSuperuser().or(false), + options.getLogin().or(false)); process(insertCql, consistencyForRoleWrite(role.getRoleName())); } @@ -511,8 +511,6 @@ public class CassandraRoleManager implements IRoleManager return String.format("is_superuser = %s", entry.getValue()); case PASSWORD: return String.format("salted_hash = '%s'", escape(hashpw((String) entry.getValue()))); - case HASHED_PASSWORD: - return String.format("salted_hash = '%s'", (String) entry.getValue()); default: return null; } diff --git a/src/java/org/apache/cassandra/auth/IRoleManager.java b/src/java/org/apache/cassandra/auth/IRoleManager.java index 688d5bb..6a65e65 100644 --- a/src/java/org/apache/cassandra/auth/IRoleManager.java +++ b/src/java/org/apache/cassandra/auth/IRoleManager.java @@ -41,7 +41,7 @@ public interface IRoleManager extends AuthCache.BulkLoader<RoleResource, Set<Rol */ public enum Option { - SUPERUSER, PASSWORD, LOGIN, OPTIONS, HASHED_PASSWORD + SUPERUSER, PASSWORD, LOGIN, OPTIONS } /** diff --git a/src/java/org/apache/cassandra/auth/RoleOptions.java b/src/java/org/apache/cassandra/auth/RoleOptions.java index c3ec56c..1205d34 100644 --- a/src/java/org/apache/cassandra/auth/RoleOptions.java +++ b/src/java/org/apache/cassandra/auth/RoleOptions.java @@ -19,13 +19,13 @@ package org.apache.cassandra.auth; import java.util.HashMap; import java.util.Map; -import java.util.Optional; + +import com.google.common.base.Optional; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.utils.FBUtilities; -import org.mindrot.jbcrypt.BCrypt; public class RoleOptions { @@ -68,7 +68,7 @@ public class RoleOptions */ public Optional<Boolean> getSuperuser() { - return Optional.ofNullable((Boolean) options.get(IRoleManager.Option.SUPERUSER)); + return Optional.fromNullable((Boolean)options.get(IRoleManager.Option.SUPERUSER)); } /** @@ -77,7 +77,7 @@ public class RoleOptions */ public Optional<Boolean> getLogin() { - return Optional.ofNullable((Boolean) options.get(IRoleManager.Option.LOGIN)); + return Optional.fromNullable((Boolean)options.get(IRoleManager.Option.LOGIN)); } /** @@ -86,16 +86,7 @@ public class RoleOptions */ public Optional<String> getPassword() { - return Optional.ofNullable((String)options.get(IRoleManager.Option.PASSWORD)); - } - - /** - * Return the string value of the hashed password option. - * @return hashed password option value - */ - public Optional<String> getHashedPassword() - { - return Optional.ofNullable((String) options.get(IRoleManager.Option.HASHED_PASSWORD)); + return Optional.fromNullable((String)options.get(IRoleManager.Option.PASSWORD)); } /** @@ -108,7 +99,7 @@ public class RoleOptions @SuppressWarnings("unchecked") public Optional<Map<String, String>> getCustomOptions() { - return Optional.ofNullable((Map<String, String>) options.get(IRoleManager.Option.OPTIONS)); + return Optional.fromNullable((Map<String, String>)options.get(IRoleManager.Option.OPTIONS)); } /** @@ -143,26 +134,6 @@ public class RoleOptions throw new InvalidRequestException(String.format("Invalid value for property '%s'. " + "It must be a string", option.getKey())); - if (options.containsKey(IRoleManager.Option.HASHED_PASSWORD)) - throw new InvalidRequestException(String.format("Properties '%s' and '%s' are mutually exclusive", - IRoleManager.Option.PASSWORD, IRoleManager.Option.HASHED_PASSWORD)); - break; - case HASHED_PASSWORD: - if (!(option.getValue() instanceof String)) - throw new InvalidRequestException(String.format("Invalid value for property '%s'. " + - "It must be a string", - option.getKey())); - if (options.containsKey(IRoleManager.Option.PASSWORD)) - throw new InvalidRequestException(String.format("Properties '%s' and '%s' are mutually exclusive", - IRoleManager.Option.PASSWORD, IRoleManager.Option.HASHED_PASSWORD)); - try - { - BCrypt.checkpw("dummy", (String) option.getValue()); - } - catch (Exception e) - { - throw new InvalidRequestException("Invalid hashed password value. Please use jBcrypt."); - } break; case OPTIONS: if (!(option.getValue() instanceof Map)) diff --git a/src/java/org/apache/cassandra/cql3/PasswordObfuscator.java b/src/java/org/apache/cassandra/cql3/PasswordObfuscator.java index 8e18f34..89962f9 100644 --- a/src/java/org/apache/cassandra/cql3/PasswordObfuscator.java +++ b/src/java/org/apache/cassandra/cql3/PasswordObfuscator.java @@ -18,7 +18,7 @@ package org.apache.cassandra.cql3; -import java.util.Optional; +import com.google.common.base.Optional; import org.apache.cassandra.auth.PasswordAuthenticator; import org.apache.cassandra.auth.RoleOptions; @@ -63,15 +63,9 @@ public class PasswordObfuscator Optional<String> pass = opts.getPassword(); if (!pass.isPresent() || pass.get().isEmpty()) - pass = opts.getHashedPassword(); - if (!pass.isPresent() || pass.get().isEmpty()) return query; - // Regular expression: - // - Match new line and case insensitive (?si), and PASSWORD_TOKEN with greedy mode up to the start of the actual password and group it. - // - Quote the password between \Q and \E so any potential special characters are ignored - // - Replace the match with the grouped data + the obfuscated token - return query.replaceAll("((?si)"+ PASSWORD_TOKEN + ".+?)\\Q" + pass.get() + "\\E", - "$1" + PasswordObfuscator.OBFUSCATION_TOKEN); + // match new line, case insensitive (?si), and PASSWORD_TOKEN up to the actual password greedy. Group that and replace the password + return query.replaceAll("((?si)"+ PASSWORD_TOKEN + ".+?)" + pass.get(), "$1" + PasswordObfuscator.OBFUSCATION_TOKEN); } } diff --git a/src/java/org/apache/cassandra/tools/HashPassword.java b/src/java/org/apache/cassandra/tools/HashPassword.java deleted file mode 100644 index c4b6314..0000000 --- a/src/java/org/apache/cassandra/tools/HashPassword.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.GnuParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.OptionGroup; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; -import org.apache.commons.lang3.StringUtils; -import org.mindrot.jbcrypt.BCrypt; - -public class HashPassword -{ - private static final String LOGROUNDS_OPTION = "logrounds"; - private static final String HELP_OPTION = "help"; - private static final String ENV_VAR = "environment-var"; - private static final String PLAIN = "plain"; - private static final String INPUT = "input"; - - private static final int LOGROUNDS_DEFAULT = 10; - private static final int MIN_PASS_LENGTH = 4; - - public static void main(String[] args) - { - try - { - Options options = getOptions(); - CommandLine cmd = parseCommandLine(args, options); - - String password = null; - if (cmd.hasOption(ENV_VAR)) - { - password = System.getenv(cmd.getOptionValue(ENV_VAR)); - if (password == null) - { - System.err.println(String.format("Environment variable '%s' is undefined.", cmd.getOptionValue(ENV_VAR))); - System.exit(1); - } - } - else if (cmd.hasOption(PLAIN)) - { - password = cmd.getOptionValue(PLAIN); - } - else if (cmd.hasOption(INPUT)) - { - String input = cmd.getOptionValue(INPUT); - byte[] fileInput = null; - if ("-".equals(input)) - { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - int rd; - while ((rd = System.in.read()) != -1) - os.write(rd); - fileInput = os.toByteArray(); - } - else - { - try - { - Path file = Paths.get(input); - fileInput = Files.readAllBytes(file); - } - catch (IOException e) - { - System.err.printf("Failed to read from '%s': %s%n", input, e); - System.exit(1); - } - } - password = new String(fileInput, StandardCharsets.UTF_8); - } - else - { - System.err.println(String.format("One of the options --%s, --%s or --%s must be used.", - ENV_VAR, PLAIN, INPUT)); - printUsage(options); - System.exit(1); - } - - if (password.chars().anyMatch(i -> i < 32)) - System.err.println("WARNING: The provided plain text password contains non-printable characters (ASCII<32)."); - - if (password.length() < MIN_PASS_LENGTH) - System.err.println("WARNING: The provided password is very short, probably too short to be secure."); - - int logRounds = cmd.hasOption(LOGROUNDS_OPTION) ? Integer.parseInt(cmd.getOptionValue(LOGROUNDS_OPTION)) : LOGROUNDS_DEFAULT; - if (logRounds < 4 || logRounds > 30) - { - System.err.println(String.format("Bad value for --%s %d. " + - "Please use a value between 4 and 30 inclusively", - LOGROUNDS_OPTION, logRounds)); - System.exit(1); - } - - // The number of rounds is in fact = 2^rounds. - if (logRounds > 16) - System.err.println(String.format("WARNING: Using a high number of hash rounds, as configured using '--%s %d' " + - "will consume a lot of CPU and likely cause timeouts. Note that the parameter defines the " + - "logarithmic number of rounds: %d becomes 2^%d = %d rounds", - LOGROUNDS_OPTION, logRounds, - logRounds, logRounds, 1 << logRounds)); - - if (password.getBytes().length > 72) - System.err.println(String.format("WARNING: The provided password has a length of %d bytes, but the underlying hash/crypt algorithm " + - "(bcrypt) can only compare up to 72 bytes. The password will be accepted and work, but only compared up to 72 bytes.", - password.getBytes().length)); - - String hashed = escape(hashpw(password, logRounds)); - System.out.print(hashed); - System.out.flush(); - } - catch (Exception e) - { - e.printStackTrace(); - System.exit(1); - } - } - - private static CommandLine parseCommandLine(String[] args, Options options) throws ParseException - { - CommandLineParser parser = new GnuParser(); - - CommandLine cmd = parser.parse(options, args, false); - - if (cmd.hasOption(HELP_OPTION)) - { - printUsage(options); - System.exit(0); - } - return cmd; - } - - private static Options getOptions() - { - Options options = new Options(); - options.addOption("h", HELP_OPTION, false, "Display this help message"); - options.addOption("r", LOGROUNDS_OPTION, true, "Number of hash rounds (default: " + LOGROUNDS_DEFAULT + ")."); - OptionGroup group = new OptionGroup(); - group.addOption(new Option("e", ENV_VAR, true, - "Use value of the specified environment variable as the password")); - group.addOption(new Option("p", PLAIN, true, - "Argument is the plain text password")); - group.addOption(new Option("i", INPUT, true, - "Input is a file (or - for stdin) to read the password from. " + - "Make sure that the whole input including newlines is considered. " + - "For example, the shell command 'echo -n foobar | hash_password -i -' will " + - "work as intended and just hash 'foobar'.")); - options.addOptionGroup(group); - return options; - } - - private static String hashpw(String password, int rounds) - { - return BCrypt.hashpw(password, BCrypt.gensalt(rounds)); - } - - private static String escape(String name) - { - return StringUtils.replace(name, "'", "''"); - } - - public static void printUsage(Options options) - { - String usage = "hash_password [options]"; - String header = "--\n" + - "Hashes a plain text password and prints the hashed password.\n" + - "Options are:"; - new HelpFormatter().printHelp(usage, header, options, ""); - } -} diff --git a/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java b/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java index 71a88e5..c4be4fb 100644 --- a/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java +++ b/test/unit/org/apache/cassandra/audit/AuditLoggerAuthTest.java @@ -61,7 +61,6 @@ public class AuditLoggerAuthTest private static final String TEST_USER = "testuser"; private static final String TEST_ROLE = "testrole"; private static final String TEST_PW = "testpassword"; - private static final String TEST_PW_HASH = "$2a$10$1fI9MDCe13ZmEYW4XXZibuASNKyqOY828ELGUtml/t.0Mk/6Kqnsq"; private static final String CASS_USER = "cassandra"; private static final String CASS_PW = "cassandra"; @@ -145,21 +144,6 @@ public class AuditLoggerAuthTest } @Test - public void testCqlCreateRoleSyntaxErrorWithHashedPwd() - { - String createTestRoleCQL = String.format("CREATE ROLE %s WITH LOGIN = %s ANDSUPERUSER = %s AND HASHED PASSWORD", - TEST_ROLE, true, false) + TEST_PW_HASH; - String createTestRoleCQLExpected = String.format("CREATE ROLE %s WITH LOGIN = %s ANDSUPERUSER = %s AND HASHED PASSWORD ", - TEST_ROLE, true, false) + PasswordObfuscator.OBFUSCATION_TOKEN; - - executeWithCredentials(Collections.singletonList(createTestRoleCQL), CASS_USER, CASS_PW, AuditLogEntryType.LOGIN_SUCCESS); - assertTrue(getInMemAuditLogger().size() > 0); - AuditLogEntry logEntry = getInMemAuditLogger().poll(); - assertLogEntry(logEntry, AuditLogEntryType.REQUEST_FAILURE, createTestRoleCQLExpected, CASS_USER, TEST_PW); - assertEquals(0, getInMemAuditLogger().size()); - } - - @Test public void testCqlALTERRoleAuditing() { createTestRole(); @@ -167,28 +151,7 @@ public class AuditLoggerAuthTest executeWithCredentials(Arrays.asList(cql), CASS_USER, CASS_PW, AuditLogEntryType.LOGIN_SUCCESS); assertTrue(getInMemAuditLogger().size() > 0); AuditLogEntry logEntry = getInMemAuditLogger().poll(); - assertLogEntry(logEntry, - AuditLogEntryType.ALTER_ROLE, - "ALTER ROLE " + TEST_ROLE + " WITH PASSWORD = '" + PasswordObfuscator.OBFUSCATION_TOKEN + "'", - CASS_USER, - "foo_bar"); - assertEquals(0, getInMemAuditLogger().size()); - } - - @Test - public void testCqlALTERRoleAuditingWithHashedPwd() - { - createTestRole(); - String cql = "ALTER ROLE " + TEST_ROLE + " WITH HASHED PASSWORD = '" + TEST_PW_HASH + "'"; - executeWithCredentials(Arrays.asList(cql), CASS_USER, CASS_PW, AuditLogEntryType.LOGIN_SUCCESS); - assertTrue(getInMemAuditLogger().size() > 0); - AuditLogEntry logEntry = getInMemAuditLogger().poll(); - assertLogEntry(logEntry, - AuditLogEntryType.ALTER_ROLE, - "ALTER ROLE " + TEST_ROLE + - " WITH HASHED PASSWORD = '" + PasswordObfuscator.OBFUSCATION_TOKEN + "'", - CASS_USER, - TEST_PW_HASH); + assertLogEntry(logEntry, AuditLogEntryType.ALTER_ROLE, "ALTER ROLE " + TEST_ROLE + " WITH PASSWORD = '" + PasswordObfuscator.OBFUSCATION_TOKEN + "'", CASS_USER, "foo_bar"); assertEquals(0, getInMemAuditLogger().size()); } @@ -297,45 +260,6 @@ public class AuditLoggerAuthTest TEST_PW); } - @Test - public void testCqlUSERCommandsAuditingWithHashedPwd() - { - //CREATE USER and ALTER USER are supported only for backwards compatibility. - - String user = TEST_ROLE + "userHasedPwd"; - String cql = "CREATE USER " + user + " WITH HASHED PASSWORD '" + TEST_PW_HASH + "'"; - executeWithCredentials(Arrays.asList(cql), CASS_USER, CASS_PW, AuditLogEntryType.LOGIN_SUCCESS); - assertTrue(getInMemAuditLogger().size() > 0); - AuditLogEntry logEntry = getInMemAuditLogger().poll(); - assertLogEntry(logEntry, - AuditLogEntryType.CREATE_ROLE, - "CREATE USER " + user + " WITH HASHED PASSWORD '" + PasswordObfuscator.OBFUSCATION_TOKEN + "'", - CASS_USER, - TEST_PW_HASH); - - cql = "ALTER USER " + user + " WITH HASHED PASSWORD '" + TEST_PW_HASH + "'"; - executeWithCredentials(Arrays.asList(cql), CASS_USER, CASS_PW, AuditLogEntryType.LOGIN_SUCCESS); - assertTrue(getInMemAuditLogger().size() > 0); - logEntry = getInMemAuditLogger().poll(); - assertLogEntry(logEntry, - AuditLogEntryType.ALTER_ROLE, - "ALTER USER " + user + " WITH HASHED PASSWORD '" + PasswordObfuscator.OBFUSCATION_TOKEN + "'", - CASS_USER, - TEST_PW_HASH); - - cql = "ALTER USER " + user + " WITH HASHED PASSWORD " + TEST_PW_HASH; - executeWithCredentials(Arrays.asList(cql), CASS_USER, CASS_PW, AuditLogEntryType.LOGIN_SUCCESS); - assertTrue(getInMemAuditLogger().size() > 0); - logEntry = getInMemAuditLogger().poll(); - assertLogEntry(logEntry, - AuditLogEntryType.REQUEST_FAILURE, - "ALTER USER " + user - + " WITH HASHED PASSWORD " + PasswordObfuscator.OBFUSCATION_TOKEN - + "; Syntax Exception. Obscured for security reasons.", - CASS_USER, - TEST_PW_HASH); - } - /** * Helper methods */ @@ -429,4 +353,4 @@ public class AuditLoggerAuthTest assertLogEntry(logEntry, AuditLogEntryType.CREATE_ROLE, getCreateRoleCql(TEST_ROLE, true, false, true), CASS_USER, ""); assertEquals(0, getInMemAuditLogger().size()); } -} +} \ No newline at end of file diff --git a/test/unit/org/apache/cassandra/auth/CreateAndAlterRoleTest.java b/test/unit/org/apache/cassandra/auth/CreateAndAlterRoleTest.java deleted file mode 100644 index 33ae129..0000000 --- a/test/unit/org/apache/cassandra/auth/CreateAndAlterRoleTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 org.junit.BeforeClass; -import org.junit.Test; - -import com.datastax.driver.core.exceptions.AuthenticationException; -import org.apache.cassandra.Util; -import org.apache.cassandra.cql3.CQLTester; - -import static org.junit.Assert.assertTrue; -import static org.mindrot.jbcrypt.BCrypt.gensalt; -import static org.mindrot.jbcrypt.BCrypt.hashpw; - -public class CreateAndAlterRoleTest extends CQLTester -{ - @BeforeClass - public static void setUpClass() - { - CQLTester.setUpClass(); - requireAuthentication(); - requireNetwork(); - } - - @Test - public void createAlterRoleWithHashedPassword() throws Throwable - { - String user1 = "hashed_pw_role"; - String user2 = "pw_role"; - String plainTextPwd = "super_secret_thing"; - String plainTextPwd2 = "much_safer_password"; - String hashedPassword = hashpw(plainTextPwd, gensalt(4)); - String hashedPassword2 = hashpw(plainTextPwd2, gensalt(4)); - - useSuperUser(); - - assertInvalidMessage("Invalid hashed password value", - String.format("CREATE ROLE %s WITH login=true AND hashed password='%s'", - user1, "this_is_an_invalid_hash")); - assertInvalidMessage("Options 'password' and 'hashed password' are mutually exclusive", - String.format("CREATE ROLE %s WITH login=true AND password='%s' AND hashed password='%s'", - user1, plainTextPwd, hashedPassword)); - executeNet(String.format("CREATE ROLE %s WITH login=true AND hashed password='%s'", user1, hashedPassword)); - executeNet(String.format("CREATE ROLE %s WITH login=true AND password='%s'", user2, plainTextPwd)); - - useUser(user1, plainTextPwd); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - - useUser(user2, plainTextPwd); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - - useSuperUser(); - - assertInvalidMessage("Options 'password' and 'hashed password' are mutually exclusive", - String.format("ALTER ROLE %s WITH password='%s' AND hashed password='%s'", - user1, plainTextPwd2, hashedPassword2)); - executeNet(String.format("ALTER ROLE %s WITH password='%s'", user1, plainTextPwd2)); - executeNet(String.format("ALTER ROLE %s WITH hashed password='%s'", user2, hashedPassword2)); - - useUser(user1, plainTextPwd2); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - - useUser(user2, plainTextPwd2); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - } - - @Test - public void createAlterUserWithHashedPassword() throws Throwable - { - String user1 = "hashed_pw_user"; - String user2 = "pw_user"; - String plainTextPwd = "super_secret_thing"; - String plainTextPwd2 = "much_safer_password"; - String hashedPassword = hashpw(plainTextPwd, gensalt(4)); - String hashedPassword2 = hashpw(plainTextPwd2, gensalt(4)); - - useSuperUser(); - - assertInvalidMessage("Invalid hashed password value", - String.format("CREATE USER %s WITH hashed password '%s'", - user1, "this_is_an_invalid_hash")); - executeNet(String.format("CREATE USER %s WITH hashed password '%s'", user1, hashedPassword)); - executeNet(String.format("CREATE USER %s WITH password '%s'", user2, plainTextPwd)); - - useUser(user1, plainTextPwd); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - - useUser(user2, plainTextPwd); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - - useSuperUser(); - - executeNet(String.format("ALTER USER %s WITH password '%s'", user1, plainTextPwd2)); - executeNet(String.format("ALTER USER %s WITH hashed password '%s'", user2, hashedPassword2)); - - useUser(user1, plainTextPwd2); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - - useUser(user2, plainTextPwd2); - - executeNetWithAuthSpin("SELECT key FROM system.local"); - } - - /** - * Altering or creating auth may take some time to be effective - * - * @param query - */ - void executeNetWithAuthSpin(String query) - { - Util.spinAssertEquals(true, () -> { - try - { - executeNet(query); - return true; - } - catch (Throwable e) - { - assertTrue("Unexpected exception: " + e, e instanceof AuthenticationException); - reinitializeNetwork(); - return false; - } - }, 10); - } -} diff --git a/test/unit/org/apache/cassandra/auth/RoleOptionsTest.java b/test/unit/org/apache/cassandra/auth/RoleOptionsTest.java index 8a224eb..6dea2b5 100644 --- a/test/unit/org/apache/cassandra/auth/RoleOptionsTest.java +++ b/test/unit/org/apache/cassandra/auth/RoleOptionsTest.java @@ -52,23 +52,10 @@ public class RoleOptionsTest assertInvalidOptions(opts, "Invalid value for property 'SUPERUSER'. It must be a boolean"); opts = new RoleOptions(); - opts.setOption(IRoleManager.Option.HASHED_PASSWORD, 99); - assertInvalidOptions(opts, "Invalid value for property 'HASHED_PASSWORD'. It must be a string"); - - opts = new RoleOptions(); - opts.setOption(IRoleManager.Option.HASHED_PASSWORD, "invalid_hash"); - assertInvalidOptions(opts, "Invalid hashed password value. Please use jBcrypt."); - - opts = new RoleOptions(); opts.setOption(IRoleManager.Option.OPTIONS, false); assertInvalidOptions(opts, "Invalid value for property 'OPTIONS'. It must be a map"); opts = new RoleOptions(); - opts.setOption(IRoleManager.Option.PASSWORD, "abc"); - opts.setOption(IRoleManager.Option.HASHED_PASSWORD, "$2a$10$JSJEMFm6GeaW9XxT5JIheuEtPvat6i7uKbnTcxX3c1wshIIsGyUtG"); - assertInvalidOptions(opts, "Properties 'PASSWORD' and 'HASHED_PASSWORD' are mutually exclusive"); - - opts = new RoleOptions(); opts.setOption(IRoleManager.Option.LOGIN, true); opts.setOption(IRoleManager.Option.SUPERUSER, false); opts.setOption(IRoleManager.Option.PASSWORD, "test"); @@ -124,7 +111,7 @@ public class RoleOptionsTest } catch (InvalidRequestException e) { - assertEquals(message, e.getMessage()); + assertTrue(e.getMessage().equals(message)); } } diff --git a/test/unit/org/apache/cassandra/cql3/PasswordObfuscatorTest.java b/test/unit/org/apache/cassandra/cql3/PasswordObfuscatorTest.java index 96f97b6..09a366e 100644 --- a/test/unit/org/apache/cassandra/cql3/PasswordObfuscatorTest.java +++ b/test/unit/org/apache/cassandra/cql3/PasswordObfuscatorTest.java @@ -29,23 +29,13 @@ import static org.junit.Assert.assertEquals; public class PasswordObfuscatorTest { - private static final RoleOptions plainPwdOpts = new RoleOptions(); - private static final RoleOptions hashedPwdOpts = new RoleOptions(); - private static final String plainPwd = "testpassword"; - private static final String hashedPwd = "$2a$10$1fI9MDCe13ZmEYW4XXZibuASNKyqOY828ELGUtml/t.0Mk/6Kqnsq"; + private static final RoleOptions opts = new RoleOptions(); + private static final String optsPassword = "testpassword"; @BeforeClass public static void startup() { - plainPwdOpts.setOption(org.apache.cassandra.auth.IRoleManager.Option.PASSWORD, plainPwd); - hashedPwdOpts.setOption(org.apache.cassandra.auth.IRoleManager.Option.HASHED_PASSWORD, hashedPwd); - } - - @Test - public void testSpecialCharsObfuscation() - { - assertEquals("ALTER ROLE testrole WITH HASHED PASSWORD = '" + OBFUSCATION_TOKEN + "'", - obfuscate(format("ALTER ROLE testrole WITH HASHED PASSWORD = '%s'",hashedPwd), hashedPwdOpts)); + opts.setOption(org.apache.cassandra.auth.IRoleManager.Option.PASSWORD, "testpassword"); } @Test @@ -55,13 +45,7 @@ public class PasswordObfuscatorTest obfuscate("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '123'")); assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s'", OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD = '" + hashedPwd + "'")); - - assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD = '%s'", OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD = '%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s'", optsPassword), opts)); } @Test @@ -71,20 +55,14 @@ public class PasswordObfuscatorTest obfuscate("CREATE ROLE role1 WITH password = '123' AND LOGIN = true")); assertEquals(format("CREATE ROLE role1 WITH password = '%s' AND LOGIN = true", OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH password = '%s' AND LOGIN = true", plainPwd), plainPwdOpts)); - - assertEquals(format("CREATE ROLE role1 WITH HASHED password %s", OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH HASHED password = '%s' AND LOGIN = true", hashedPwd))); - - assertEquals(format("CREATE ROLE role1 WITH HASHED password = '%s' AND LOGIN = true", OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH HASHED password = '%s' AND LOGIN = true", hashedPwd), hashedPwdOpts)); + obfuscate(format("CREATE ROLE role1 WITH password = '%s' AND LOGIN = true", optsPassword), opts)); } @Test public void testCreateRoleWithoutPassword() { assertEquals("CREATE ROLE role1", obfuscate("CREATE ROLE role1")); - assertEquals("CREATE ROLE role1", obfuscate("CREATE ROLE role1", plainPwdOpts)); + assertEquals("CREATE ROLE role1", obfuscate("CREATE ROLE role1", opts)); } @Test @@ -92,21 +70,13 @@ public class PasswordObfuscatorTest { assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD %s", OBFUSCATION_TOKEN), obfuscate("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '123';" + - "CREATE ROLE role2 WITH LOGIN = true AND PASSWORD = '123'")); - - assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s';" + - "CREATE ROLE role2 WITH LOGIN = true AND PASSWORD = '%s'", OBFUSCATION_TOKEN, OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s';" + - "CREATE ROLE role2 WITH LOGIN = true AND PASSWORD = '%s'", plainPwd, plainPwd), plainPwdOpts)); + "CREATE ROLE role2 WITH LOGIN = true AND PASSWORD = '123'")); - assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD = '%s';" + - "CREATE ROLE role2 WITH LOGIN = true AND HASHED PASSWORD = '%s'", hashedPwd, hashedPwd))); - - assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD = '%s';" + - "CREATE ROLE role2 WITH LOGIN = true AND HASHED PASSWORD = '%s'", OBFUSCATION_TOKEN, OBFUSCATION_TOKEN), - obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND HASHED PASSWORD = '%s';" + - "CREATE ROLE role2 WITH LOGIN = true AND HASHED PASSWORD = '%s'", hashedPwd, hashedPwd), hashedPwdOpts)); + assertEquals(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s';" + + "CREATE ROLE role2 WITH LOGIN = true AND PASSWORD = '%s'", OBFUSCATION_TOKEN, OBFUSCATION_TOKEN), + obfuscate(format("CREATE ROLE role1 WITH LOGIN = true AND PASSWORD = '%s';" + + "CREATE ROLE role2 WITH LOGIN = true AND PASSWORD = '%s'", optsPassword, optsPassword), + opts)); } @Test @@ -116,13 +86,7 @@ public class PasswordObfuscatorTest obfuscate("ALTER ROLE role1 with PASSWORD = '123'")); assertEquals(format("ALTER ROLE role1 with PASSWORD = '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with PASSWORD = '%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("ALTER ROLE role1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with HASHED PASSWORD = '%s'", hashedPwd))); - - assertEquals(format("ALTER ROLE role1 with HASHED PASSWORD = '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with HASHED PASSWORD = '%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("ALTER ROLE role1 with PASSWORD = '%s'", optsPassword), opts)); } @Test @@ -132,13 +96,7 @@ public class PasswordObfuscatorTest obfuscate("ALTER ROLE role1 with PASSWORD='123'")); assertEquals(format("ALTER ROLE role1 with PASSWORD='%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with PASSWORD='%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("ALTER ROLE role1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with HASHED PASSWORD='%s'", hashedPwd))); - - assertEquals(format("ALTER ROLE role1 with HASHED PASSWORD='%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with HASHED PASSWORD='%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("ALTER ROLE role1 with PASSWORD='%s'", optsPassword), opts)); } @Test @@ -148,13 +106,7 @@ public class PasswordObfuscatorTest obfuscate("ALTER ROLE role1 with PASSWORD= '123'")); assertEquals(format("ALTER ROLE role1 with PASSWORD= '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with PASSWORD= '%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("ALTER ROLE role1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with HASHED PASSWORD= '%s'", hashedPwd))); - - assertEquals(format("ALTER ROLE role1 with HASHED PASSWORD= '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER ROLE role1 with HASHED PASSWORD= '%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("ALTER ROLE role1 with PASSWORD= '%s'", optsPassword), opts)); } @Test @@ -162,9 +114,7 @@ public class PasswordObfuscatorTest { assertEquals("ALTER ROLE role1", obfuscate("ALTER ROLE role1")); - assertEquals("ALTER ROLE role1", obfuscate("ALTER ROLE role1", plainPwdOpts)); - - assertEquals("ALTER ROLE role1", obfuscate("ALTER ROLE role1", hashedPwdOpts)); + assertEquals("ALTER ROLE role1", obfuscate("ALTER ROLE role1", opts)); } @Test @@ -174,13 +124,7 @@ public class PasswordObfuscatorTest obfuscate("CREATE USER user1 with PASSWORD '123'")); assertEquals(format("CREATE USER user1 with PASSWORD '%s'", OBFUSCATION_TOKEN), - obfuscate(format("CREATE USER user1 with PASSWORD '%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("CREATE USER user1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("CREATE USER user1 with HASHED PASSWORD '%s'", hashedPwd))); - - assertEquals(format("CREATE USER user1 with HASHED PASSWORD '%s'", OBFUSCATION_TOKEN), - obfuscate(format("CREATE USER user1 with HASHED PASSWORD '%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("CREATE USER user1 with PASSWORD '%s'", optsPassword), opts)); } @Test @@ -188,9 +132,7 @@ public class PasswordObfuscatorTest { assertEquals("CREATE USER user1", obfuscate("CREATE USER user1")); - assertEquals("CREATE USER user1", obfuscate("CREATE USER user1", plainPwdOpts)); - - assertEquals("CREATE USER user1", obfuscate("CREATE USER user1", hashedPwdOpts)); + assertEquals("CREATE USER user1", obfuscate("CREATE USER user1", opts)); } @Test @@ -200,13 +142,7 @@ public class PasswordObfuscatorTest obfuscate("ALTER USER user1 with PASSWORD '123'")); assertEquals(format("ALTER USER user1 with PASSWORD '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with PASSWORD '%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("ALTER USER user1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with HASHED PASSWORD '%s'", hashedPwd))); - - assertEquals(format("ALTER USER user1 with HASHED PASSWORD '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with HASHED PASSWORD '%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("ALTER USER user1 with PASSWORD '%s'", optsPassword), opts)); } @Test @@ -216,13 +152,7 @@ public class PasswordObfuscatorTest obfuscate("ALTER USER user1 with paSSwoRd '123'")); assertEquals(format("ALTER USER user1 with paSSwoRd '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with paSSwoRd '%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("ALTER USER user1 with HASHED paSSwoRd %s", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with HASHED paSSwoRd '%s'", hashedPwd))); - - assertEquals(format("ALTER USER user1 with HASHED paSSwoRd '%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with HASHED paSSwoRd '%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("ALTER USER user1 with paSSwoRd '%s'", optsPassword), opts)); } @Test @@ -232,13 +162,7 @@ public class PasswordObfuscatorTest obfuscate("ALTER USER user1 with PASSWORD\n'123'")); assertEquals(format("ALTER USER user1 with PASSWORD\n'%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with PASSWORD\n'%s'", plainPwd), plainPwdOpts)); - - assertEquals(format("ALTER USER user1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with HASHED PASSWORD\n'%s'", hashedPwd))); - - assertEquals(format("ALTER USER user1 with HASHED PASSWORD\n'%s'", OBFUSCATION_TOKEN), - obfuscate(format("ALTER USER user1 with HASHED PASSWORD\n'%s'", hashedPwd), hashedPwdOpts)); + obfuscate(format("ALTER USER user1 with PASSWORD\n'%s'", optsPassword), opts)); } @Test @@ -251,12 +175,6 @@ public class PasswordObfuscatorTest newLinePassOpts.setOption(org.apache.cassandra.auth.IRoleManager.Option.PASSWORD, "test\npassword"); assertEquals(String.format("CREATE USER user1 with PASSWORD '%s'", OBFUSCATION_TOKEN), obfuscate(format("CREATE USER user1 with PASSWORD '%s'", "test\npassword"), newLinePassOpts)); - - assertEquals(String.format("CREATE USER user1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate("CREATE USER user1 with HASHED PASSWORD 'a\nb'")); - - assertEquals(String.format("CREATE USER user1 with HASHED PASSWORD '%s'", OBFUSCATION_TOKEN), - obfuscate(format("CREATE USER user1 with HASHED PASSWORD '%s'", "test\npassword"), newLinePassOpts)); } @Test @@ -269,12 +187,6 @@ public class PasswordObfuscatorTest emptyPassOpts.setOption(org.apache.cassandra.auth.IRoleManager.Option.PASSWORD, ""); assertEquals("CREATE USER user1 with PASSWORD ''", obfuscate("CREATE USER user1 with PASSWORD ''", emptyPassOpts)); - - assertEquals(String.format("CREATE USER user1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate("CREATE USER user1 with HASHED PASSWORD ''")); - - assertEquals("CREATE USER user1 with HASHED PASSWORD ''", - obfuscate("CREATE USER user1 with HASHED PASSWORD ''", emptyPassOpts)); } @Test @@ -282,9 +194,6 @@ public class PasswordObfuscatorTest { assertEquals(String.format("CREATE USER user1 with PASSWORD %s", OBFUSCATION_TOKEN), obfuscate("CREATE USER user1 with PASSWORD 'p a ss wor d'")); - - assertEquals(String.format("CREATE USER user1 with HASHED PASSWORD %s", OBFUSCATION_TOKEN), - obfuscate("CREATE USER user1 with HASHED PASSWORD 'p a ss wor d'")); } @Test @@ -302,23 +211,8 @@ public class PasswordObfuscatorTest "APPLY BATCH;", OBFUSCATION_TOKEN), obfuscate(format("BEGIN BATCH \n" + " CREATE ROLE alice1 WITH PASSWORD = '%s' and LOGIN = true; \n" + - "APPLY BATCH;", plainPwd), - plainPwdOpts)); - - assertEquals(format("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD %s", - OBFUSCATION_TOKEN), - obfuscate("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD = '" + hashedPwd + "' and LOGIN = true; \n" + - "APPLY BATCH;")); - - assertEquals(format("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD = '%s' and LOGIN = true; \n" + - "APPLY BATCH;", OBFUSCATION_TOKEN), - obfuscate(format("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD = '%s' and LOGIN = true; \n" + - "APPLY BATCH;", hashedPwd), - hashedPwdOpts)); + "APPLY BATCH;", optsPassword), + opts)); } @Test @@ -340,26 +234,7 @@ public class PasswordObfuscatorTest obfuscate(format("BEGIN BATCH \n" + " CREATE ROLE alice1 WITH PASSWORD = '%s' and LOGIN = true; \n" + " CREATE ROLE alice2 WITH PASSWORD = '%s' and LOGIN = true; \n" + - "APPLY BATCH;", plainPwd, plainPwd), - plainPwdOpts)); - - assertEquals(format("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD %s", - OBFUSCATION_TOKEN), - obfuscate("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD = '" + hashedPwd + "' and LOGIN = true; \n" + - " CREATE ROLE alice2 WITH HASHED PASSWORD = '" + hashedPwd + "' and LOGIN = true; \n" + - "APPLY BATCH;")); - - assertEquals(format("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD = '%s' and LOGIN = true; \n" + - " CREATE ROLE alice2 WITH HASHED PASSWORD = '%s' and LOGIN = true; \n" + - "APPLY BATCH;" - , OBFUSCATION_TOKEN, OBFUSCATION_TOKEN), - obfuscate(format("BEGIN BATCH \n" + - " CREATE ROLE alice1 WITH HASHED PASSWORD = '%s' and LOGIN = true; \n" + - " CREATE ROLE alice2 WITH HASHED PASSWORD = '%s' and LOGIN = true; \n" + - "APPLY BATCH;", hashedPwd, hashedPwd), - hashedPwdOpts)); + "APPLY BATCH;", optsPassword, optsPassword), + opts)); } } diff --git a/test/unit/org/apache/cassandra/tools/HashPasswordTest.java b/test/unit/org/apache/cassandra/tools/HashPasswordTest.java deleted file mode 100644 index f84bd8b..0000000 --- a/test/unit/org/apache/cassandra/tools/HashPasswordTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import org.apache.cassandra.cql3.CQLTester; -import org.apache.cassandra.tools.ToolRunner.ToolResult; -import org.mindrot.jbcrypt.BCrypt; - -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -public class HashPasswordTest extends CQLTester -{ - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - private static final String plaintextPassword = "foobar"; - private static final String hashPasswordTool = "tools/bin/hash_password"; - - /* If help changed you also need to change the docs*/ - @Test - public void testHelpAndShouldChangeDocs() - { - ToolResult tool = ToolRunner.invoke(hashPasswordTool, "-h"); - tool.assertOnCleanExit(); - String help = "usage: hash_password [options]\n" + - "--\n" + - "Hashes a plain text password and prints the hashed password.\n" + - "Options are:\n" + - " -e,--environment-var <arg> Use value of the specified environment\n" + - " variable as the password\n" + - " -h,--help Display this help message\n" + - " -i,--input <arg> Input is a file (or - for stdin) to read the\n" + - " password from. Make sure that the whole input including newlines is\n" + - " considered. For example, the shell command 'echo -n foobar | hash_password\n" + - " -i -' will work as intended and just hash 'foobar'.\n" + - " -p,--plain <arg> Argument is the plain text password\n" + - " -r,--logrounds <arg> Number of hash rounds (default: 10).\n"; - assertEquals(help, tool.getStdout()); - } - - @Test - public void testPlain() - { - ToolResult tool = ToolRunner.invoke(hashPasswordTool, "--plain", plaintextPassword); - tool.assertOnCleanExit(); - String hashed = tool.getStdout(); - assertTrue("Hashed password does not validate: " + hashed, BCrypt.checkpw(plaintextPassword, hashed)); - } - - @Test - public void testStdIn() - { - ToolResult tool = ToolRunner.invoke(Collections.emptyMap(), - new ByteArrayInputStream(plaintextPassword.getBytes()), - Arrays.asList(hashPasswordTool, "--input", "-")); - tool.assertOnCleanExit(); - String hashed = tool.getStdout(); - assertTrue("Hashed password does not validate: " + hashed, BCrypt.checkpw(plaintextPassword, hashed)); - } - - @Test - public void testFile() throws IOException - { - File file = temporaryFolder.newFile(); - Files.write(file.toPath(), plaintextPassword.getBytes()); - - ToolResult tool = ToolRunner.invoke(hashPasswordTool, "--input", file.getAbsolutePath()); - tool.assertOnCleanExit(); - String hashed = tool.getStdout(); - assertTrue("Hashed password does not validate: " + hashed, BCrypt.checkpw(plaintextPassword, hashed)); - } - - @Test - public void testEnvVar() - { - ToolResult tool = ToolRunner.invoke(Collections.singletonMap("THE_PASSWORD", plaintextPassword), - null, - Arrays.asList(hashPasswordTool, "--environment-var", "THE_PASSWORD")); - tool.assertOnCleanExit(); - String hashed = tool.getStdout(); - assertTrue("Hashed password does not validate: " + hashed, BCrypt.checkpw(plaintextPassword, hashed)); - } - - @Test - public void testLogRounds() - { - ToolResult tool = ToolRunner.invoke(hashPasswordTool, "--plain", plaintextPassword, "-r", "10"); - tool.assertOnCleanExit(); - String hashed = tool.getStdout(); - assertTrue("Hashed password does not validate: " + hashed, BCrypt.checkpw(plaintextPassword, hashed)); - } - - @Test - public void testShortPass() - { - ToolResult tool = ToolRunner.invoke(hashPasswordTool, "--plain", "A"); - tool.assertOnExitCode(); - assertThat(tool.getStderr(), containsString("password is very short")); - } - - @Test - public void testErrorMessages() - { - assertToolError("One of the options --environment-var, --plain or --input must be used.", hashPasswordTool); - assertToolError("Environment variable 'non_existing_environment_variable_name' is undefined.", - hashPasswordTool, - "--environment-var", - "non_existing_environment_variable_name"); - assertToolError("Failed to read from '/foo/bar/baz/blah/yadda': ", - hashPasswordTool, - "--input", - "/foo/bar/baz/blah/yadda"); - } - - private static void assertToolError(String expectedMessage, String... args) - { - ToolResult tool = ToolRunner.invoke(args); - assertEquals(1, tool.getExitCode()); - assertThat(tool.getStderr(), containsString(expectedMessage)); - } -} diff --git a/test/unit/org/apache/cassandra/tools/ToolRunner.java b/test/unit/org/apache/cassandra/tools/ToolRunner.java index f650f34..0ad88ef 100644 --- a/test/unit/org/apache/cassandra/tools/ToolRunner.java +++ b/test/unit/org/apache/cassandra/tools/ToolRunner.java @@ -119,13 +119,11 @@ public class ToolRunner private final InputStream input; private final T out; - private final boolean autoCloseOut; - private StreamGobbler(InputStream input, T out, boolean autoCloseOut) + private StreamGobbler(InputStream input, T out) { this.input = input; this.out = out; - this.autoCloseOut = autoCloseOut; } public void run() @@ -138,8 +136,6 @@ public class ToolRunner int read = input.read(buffer); if (read == -1) { - if (autoCloseOut) - out.close(); return; } out.write(buffer, 0, read); @@ -540,19 +536,19 @@ public class ToolRunner if (includeStdinWatcher) numWatchers = 3; ioWatchers = new Thread[numWatchers]; - ioWatchers[0] = new Thread(new StreamGobbler<>(process.getErrorStream(), err, false)); + ioWatchers[0] = new Thread(new StreamGobbler<>(process.getErrorStream(), err)); ioWatchers[0].setDaemon(true); ioWatchers[0].setName("IO Watcher stderr"); ioWatchers[0].start(); - ioWatchers[1] = new Thread(new StreamGobbler<>(process.getInputStream(), out, false)); + ioWatchers[1] = new Thread(new StreamGobbler<>(process.getInputStream(), out)); ioWatchers[1].setDaemon(true); ioWatchers[1].setName("IO Watcher stdout"); ioWatchers[1].start(); if (includeStdinWatcher) { - ioWatchers[2] = new Thread(new StreamGobbler<>(stdin, process.getOutputStream(), true)); + ioWatchers[2] = new Thread(new StreamGobbler<>(stdin, process.getOutputStream())); ioWatchers[2].setDaemon(true); ioWatchers[2].setName("IO Watcher stdin"); ioWatchers[2].start(); diff --git a/tools/bin/hash_password b/tools/bin/hash_password deleted file mode 100755 index dc48ce1..0000000 --- a/tools/bin/hash_password +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh - -# 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. - -if [ "x$CASSANDRA_INCLUDE" = "x" ]; then - # Locations (in order) to use when searching for an include file. - for include in "`dirname "$0"`/cassandra.in.sh" \ - "$HOME/.cassandra.in.sh" \ - /usr/share/cassandra/cassandra.in.sh \ - /usr/local/share/cassandra/cassandra.in.sh \ - /opt/cassandra/cassandra.in.sh; do - if [ -r "$include" ]; then - . "$include" - break - fi - done -elif [ -r "$CASSANDRA_INCLUDE" ]; then - . "$CASSANDRA_INCLUDE" -fi - -if [ -z "$CLASSPATH" ]; then - echo "You must set the CLASSPATH var" >&2 - exit 1 -fi - -if [ "x${MAX_HEAP_SIZE}" = "x" ]; then - MAX_HEAP_SIZE="256M" -fi - -if [ "x${MAX_DIRECT_MEMORY}" = "x" ]; then - MAX_DIRECT_MEMORY="2G" -fi - -JVM_OPTS="${JVM_OPTS} -Xmx${MAX_HEAP_SIZE} -XX:MaxDirectMemorySize=${MAX_DIRECT_MEMORY}" - -"${JAVA}" ${JAVA_AGENT} -ea -cp "${CLASSPATH}" ${JVM_OPTS} \ - -Dcassandra.storagedir="${cassandra_storagedir}" \ - -Dlogback.configurationFile=logback-tools.xml \ - org.apache.cassandra.tools.HashPassword "$@" --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org