Explorar o código

fixes #1018, references #979

Sebastian Stenzel %!s(int64=5) %!d(string=hai) anos
pai
achega
a2f3f5d254

+ 15 - 0
main/commons/src/main/java/org/cryptomator/common/Environment.java

@@ -25,6 +25,7 @@ public class Environment {
 	private static final Path RELATIVE_HOME_DIR = Paths.get("~");
 	private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME);
 	private static final char PATH_LIST_SEP = ':';
+	private static final int DEFAULT_MIN_PW_LENGTH = 8;
 
 	@Inject
 	public Environment() {
@@ -37,6 +38,7 @@ public class Environment {
 		LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
 		LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
 		LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
+		LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
 	}
 
 	public boolean useCustomLogbackConfig() {
@@ -63,6 +65,19 @@ public class Environment {
 		return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
 	}
 
+	public int getMinPwLength() {
+		return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH);
+	}
+
+	private int getInt(String propertyName, int defaultValue) {
+		String value = System.getProperty(propertyName);
+		try {
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e) { // includes "null" values
+			return defaultValue;
+		}
+	}
+
 	private Optional<Path> getPath(String propertyName) {
 		String value = System.getProperty(propertyName);
 		return Optional.ofNullable(value).map(Paths::get);

+ 12 - 8
main/ui/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java

@@ -9,6 +9,7 @@
 package org.cryptomator.ui.common;
 
 import com.nulabinc.zxcvbn.Zxcvbn;
+import org.cryptomator.common.Environment;
 import org.cryptomator.ui.fxapp.FxApplicationScoped;
 
 import javax.inject.Inject;
@@ -20,30 +21,33 @@ public class PasswordStrengthUtil {
 
 	private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length
 	private static final String RESSOURCE_PREFIX = "passwordStrength.messageLabel.";
+	private static final List<String> SANITIZED_INPUTS = List.of("cryptomator");
 
-	private final Zxcvbn zxcvbn;
-	private final List<String> sanitizedInputs;
 	private final ResourceBundle resourceBundle;
+	private final int minPwLength;
+	private final Zxcvbn zxcvbn;
 
 	@Inject
-	public PasswordStrengthUtil(ResourceBundle resourceBundle) {
+	public PasswordStrengthUtil(ResourceBundle resourceBundle, Environment environment) {
 		this.resourceBundle = resourceBundle;
+		this.minPwLength = environment.getMinPwLength();
 		this.zxcvbn = new Zxcvbn();
-		this.sanitizedInputs = List.of("cryptomator");
 	}
 
 	public int computeRate(CharSequence password) {
-		if (password == null || password.length() == 0) {
+		if (password == null || password.length() < minPwLength) {
 			return -1;
 		} else {
 			int numCharsToRate = Math.min(PW_TRUNC_LEN, password.length());
-			return zxcvbn.measure(password.subSequence(0, numCharsToRate), sanitizedInputs).getScore();
+			return zxcvbn.measure(password.subSequence(0, numCharsToRate), SANITIZED_INPUTS).getScore();
 		}
 	}
 
 	public String getStrengthDescription(Number score) {
-		if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) {
-			return resourceBundle.getString("passwordStrength.messageLabel." + score.intValue());
+		if (score.intValue() == -1) {
+			return String.format(resourceBundle.getString(RESSOURCE_PREFIX + "tooShort"), minPwLength);
+		} else if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) {
+			return resourceBundle.getString(RESSOURCE_PREFIX + score.intValue());
 		} else {
 			return "";
 		}

+ 1 - 0
main/ui/src/main/resources/i18n/strings.properties

@@ -193,6 +193,7 @@ newPassword.promptText=Enter a new password
 newPassword.reenterPassword=Confirm the new password
 newPassword.passwordsMatch=Passwords match!
 newPassword.passwordsDoNotMatch=Passwords do not match
+passwordStrength.messageLabel.tooShort=Use at least %d characters
 passwordStrength.messageLabel.0=Very weak
 passwordStrength.messageLabel.1=Weak
 passwordStrength.messageLabel.2=Fair

+ 4 - 3
main/ui/src/test/java/org/cryptomator/ui/common/PasswordStrengthUtilTest.java

@@ -1,6 +1,7 @@
 package org.cryptomator.ui.common;
 
 import com.google.common.base.Strings;
+import org.cryptomator.common.Environment;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -13,7 +14,7 @@ public class PasswordStrengthUtilTest {
 
 	@Test
 	public void testLongPasswords() {
-		PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(ResourceBundle.class));
+		PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(ResourceBundle.class), Mockito.mock(Environment.class));
 		String longPw = Strings.repeat("x", 10_000);
 		Assertions.assertTimeout(Duration.ofSeconds(5), () -> {
 			util.computeRate(longPw);
@@ -21,9 +22,9 @@ public class PasswordStrengthUtilTest {
 	}
 	
 	@Test
-	@Disabled("waiting on upstream fix")
+	@Disabled("waiting on upstream fix") // https://github.com/nulab/zxcvbn4j/issues/54
 	public void testIssue979() {
-		PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(ResourceBundle.class));
+		PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(ResourceBundle.class), Mockito.mock(Environment.class));
 		int result1 = util.computeRate("backed derrick buckling mountains glove client procedures desire destination sword hidden ram");
 		int result2 = util.computeRate("backed derrick buckling mountains glove client procedures desire destination sword hidden ram escalation");
 		Assertions.assertEquals(4, result1);