Merge branch 'develop' into feature/hub

# Conflicts:
#	.github/workflows/release.yml
#	.idea/runConfigurations/Cryptomator_Linux.xml
#	.idea/runConfigurations/Cryptomator_Linux_Dev.xml
#	.idea/runConfigurations/Cryptomator_Windows.xml
#	.idea/runConfigurations/Cryptomator_Windows_Dev.xml
#	.idea/runConfigurations/Cryptomator_macOS.xml
#	.idea/runConfigurations/Cryptomator_macOS_Dev.xml
#	pom.xml
#	src/main/java/module-info.java
#	src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/SelectMasterkeyFileController.java
#	src/main/resources/license/THIRD-PARTY.txt
This commit is contained in:
Sebastian Stenzel
2022-04-08 20:27:02 +02:00
202 changed files with 4169 additions and 2620 deletions
@@ -0,0 +1,129 @@
package org.cryptomator.common;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
public class PassphraseTest {
@ParameterizedTest
@CsvSource(value = {
"-1, 0",
"0, -1",
"0, 10",
"10, 0",
"10, 10"
})
public void testInvalidConstructorArgs(int offset, int length) {
char[] data = "test".toCharArray();
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> {
new Passphrase(data, offset, length);
});
}
@ParameterizedTest
@CsvSource(value = {
"0, 4",
"0, 0",
"0, 1",
"1, 1",
"2, 2"
})
public void testValidConstructorArgs(int offset, int length) {
char[] data = "test".toCharArray();
var pw = new Passphrase(data, offset, length);
Assertions.assertEquals(length, pw.length());
Assertions.assertEquals("test".substring(offset, offset + length), pw.toString());
}
@Nested
public class InstanceMethods {
private Passphrase pw1;
private Passphrase pw2;
@BeforeEach
public void setup() {
char[] foo = "test test".toCharArray();
pw1 = new Passphrase(foo, 5, 4);
pw2 = Passphrase.copyOf("test");
}
@Test
public void testToString() {
Assertions.assertEquals("test", pw1.toString());
Assertions.assertEquals("test", pw2.toString());
}
@Test
public void testEquals() {
Assertions.assertEquals(pw1, pw2);
}
@Test
public void testHashcode() {
Assertions.assertEquals(pw1.hashCode(), pw2.hashCode());
}
@Test
public void testLength() {
Assertions.assertEquals(4, pw1.length());
Assertions.assertEquals(4, pw2.length());
}
@Test
public void testCharAt() {
Assertions.assertEquals('s', pw1.charAt(2));
}
@ParameterizedTest
@ValueSource(ints = {-1, 4, 5})
public void testInvalidCharAt(int idx) {
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> pw1.charAt(idx));
}
@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3})
public void testValidCharAt(int idx) {
Assertions.assertEquals("test".charAt(idx), pw1.charAt(idx));
}
@ParameterizedTest
@CsvSource(value = {
"-1, 0",
"0, -1",
"-1, -1",
"0, 5",
"3, 2"
})
public void testInvalidSubSequence(int start, int end) {
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> pw1.subSequence(start, end));
}
@ParameterizedTest
@CsvSource(value = {
"0, 4",
"1, 4",
"0, 2",
"2, 4",
"4, 4",
})
public void testValidSubSequence(int start, int end) {
Assertions.assertEquals("test".substring(start, end), pw1.subSequence(start, end).toString());
}
@Test
public void testDestroy() {
pw2.destroy();
Assertions.assertFalse(pw1.isDestroyed());
Assertions.assertTrue(pw2.isDestroyed());
Assertions.assertNotEquals(pw1, pw2);
}
}
}
@@ -2,7 +2,9 @@ package org.cryptomator.common.keychain;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
@@ -32,7 +34,8 @@ public class KeychainManagerTest {
public static void startup() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Platform.startup(latch::countDown);
latch.await(5, TimeUnit.SECONDS);
var javafxStarted = latch.await(5, TimeUnit.SECONDS);
Assumptions.assumeTrue(javafxStarted);
}
@Test
@@ -0,0 +1,187 @@
package org.cryptomator.common.mountpoint;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.MountPointRequirement;
import org.cryptomator.common.vaults.Volume;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class CustomMountPointChooserTest {
//--- Mocks ---
VaultSettings vaultSettings;
Environment environment;
Volume volume;
CustomMountPointChooser customMpc;
@BeforeEach
public void init() {
this.volume = Mockito.mock(Volume.class);
this.vaultSettings = Mockito.mock(VaultSettings.class);
this.environment = Mockito.mock(Environment.class);
this.customMpc = new CustomMountPointChooser(vaultSettings);
}
@Nested
public class WinfspPreperations {
@Test
@DisplayName("Hideaway name for PARENT_NO_MOUNTPOINT is not the same as mountpoint")
public void testGetHideaway() {
//prepare
Path mntPoint = Path.of("/foo/bar");
//execute
var hideaway = customMpc.getHideaway(mntPoint);
//eval
Assertions.assertNotEquals(hideaway.getFileName(), mntPoint.getFileName());
Assertions.assertEquals(hideaway.getParent(), mntPoint.getParent());
Assertions.assertTrue(hideaway.getFileName().toString().contains(mntPoint.getFileName().toString()));
}
@Test
@DisplayName("PARENT_NO_MOUNTPOINT preparations succeeds, if only mountpoint is present")
public void testPrepareParentNoMountpointOnlyMountpoint(@TempDir Path tmpDir) throws IOException {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
Files.createDirectory(mntPoint);
//execute
Assertions.assertDoesNotThrow(() -> customMpc.prepareParentNoMountPoint(mntPoint));
//evaluate
Assertions.assertTrue(Files.notExists(mntPoint));
Path hideaway = customMpc.getHideaway(mntPoint);
Assertions.assertTrue(Files.exists(hideaway));
if(OS.WINDOWS.isCurrentOs()) {
Assertions.assertTrue((Boolean) Files.getAttribute(hideaway, "dos:hidden"));
}
}
@Test
@DisplayName("PARENT_NO_MOUNTPOINT preparations fail, if only non-empty mountpoint is present")
public void testPrepareParentNoMountpointOnlyNonEmptyMountpoint(@TempDir Path tmpDir) throws IOException {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
Files.createDirectory(mntPoint);
Files.createFile(mntPoint.resolve("foo"));
//execute
Assertions.assertThrows(InvalidMountPointException.class, () -> customMpc.prepareParentNoMountPoint(mntPoint));
//evaluate
Assertions.assertTrue(Files.exists(mntPoint.resolve("foo")));
}
@Test
@DisplayName("PARENT_NO_MOUNTPOINT preparation succeeds, if for any reason only hideaway dir is present")
public void testPrepareParentNoMountpointOnlyHideaway(@TempDir Path tmpDir) throws IOException {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
var hideaway = customMpc.getHideaway(mntPoint);
Files.createDirectory(hideaway); //we explicitly do not set the file attributes here
//execute
Assertions.assertDoesNotThrow(() -> customMpc.prepareParentNoMountPoint(mntPoint));
//evaluate
Assertions.assertTrue(Files.exists(hideaway));
if(OS.WINDOWS.isCurrentOs()) {
Assertions.assertTrue((Boolean) Files.getAttribute(hideaway, "dos:hidden"));
}
}
@Test
@DisplayName("PARENT_NO_MOUNTPOINT preparation fails, if mountpoint and hideaway dirs are present")
public void testPrepareParentNoMountpointMountPointAndHideaway(@TempDir Path tmpDir) throws IOException {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
var hideaway = customMpc.getHideaway(mntPoint);
Files.createDirectory(hideaway); //we explicitly do not set the file attributes here
Files.createDirectory(mntPoint);
//execute
Assertions.assertThrows(InvalidMountPointException.class, () -> customMpc.prepareParentNoMountPoint(mntPoint));
//evaluate
Assertions.assertTrue(Files.exists(hideaway));
Assertions.assertTrue(Files.exists(mntPoint));
if(OS.WINDOWS.isCurrentOs()) {
Assertions.assertFalse((Boolean) Files.getAttribute(hideaway, "dos:hidden"));
}
}
@Test
@DisplayName("PARENT_NO_MOUNTPOINT preparation fails, if neither mountpoint nor hideaway dir is present")
public void testPrepareParentNoMountpointNothing(@TempDir Path tmpDir) {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
var hideaway = customMpc.getHideaway(mntPoint);
//execute
Assertions.assertThrows(InvalidMountPointException.class, () -> customMpc.prepareParentNoMountPoint(mntPoint));
//evaluate
Assertions.assertTrue(Files.notExists(hideaway));
Assertions.assertTrue(Files.notExists(mntPoint));
}
@Test
@DisplayName("Normal Cleanup for PARENT_NO_MOUNTPOINT")
public void testCleanupSuccess(@TempDir Path tmpDir) throws IOException {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
var hideaway = customMpc.getHideaway(mntPoint);
Files.createDirectory(hideaway);
Mockito.when(volume.getMountPointRequirement()).thenReturn(MountPointRequirement.PARENT_NO_MOUNT_POINT);
//execute
Assertions.assertDoesNotThrow(() -> customMpc.cleanup(volume, mntPoint));
//evaluate
Assertions.assertTrue(Files.exists(mntPoint));
Assertions.assertTrue(Files.notExists(hideaway));
if(OS.WINDOWS.isCurrentOs()) {
Assertions.assertFalse((Boolean) Files.getAttribute(mntPoint, "dos:hidden"));
}
}
@Test
@DisplayName("On IOException cleanup for PARENT_NO_MOUNTPOINT exits normally")
public void testCleanupIOFailure(@TempDir Path tmpDir) throws IOException {
//prepare
var mntPoint = tmpDir.resolve("mntPoint");
var hideaway = customMpc.getHideaway(mntPoint);
Files.createDirectory(hideaway);
Mockito.when(volume.getMountPointRequirement()).thenReturn(MountPointRequirement.PARENT_NO_MOUNT_POINT);
try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class)) {
filesMock.when(() -> Files.move(Mockito.any(), Mockito.any(), Mockito.any())).thenThrow(new IOException("error"));
//execute
Assertions.assertDoesNotThrow(() -> customMpc.cleanup(volume, mntPoint));
}
}
}
}
@@ -29,6 +29,7 @@ public class SettingsJsonAdapterTest {
],
"checkForUpdatesEnabled": true,
"port": 8080,
"language": "de-DE",
"numTrayNotifications": 42,
"preferredVolumeImpl": "FUSE"
}
@@ -39,6 +40,7 @@ public class SettingsJsonAdapterTest {
Assertions.assertTrue(settings.checkForUpdates().get());
Assertions.assertEquals(2, settings.getDirectories().size());
Assertions.assertEquals(8080, settings.port().get());
Assertions.assertEquals("de-DE", settings.languageProperty().get());
Assertions.assertEquals(42, settings.numTrayNotifications().get());
Assertions.assertEquals(WebDavUrlScheme.DAV, settings.preferredGvfsScheme().get());
Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get());
@@ -5,7 +5,6 @@
*******************************************************************************/
package org.cryptomator.launcher;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Assertions;
@@ -43,7 +42,7 @@ public class FileOpenRequestHandlerTest {
AppLaunchEvent evt = queue.poll();
Assertions.assertNotNull(evt);
Collection<Path> paths = evt.getPathsToOpen();
Collection<Path> paths = evt.pathsToOpen();
MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
}
@@ -0,0 +1,31 @@
package org.cryptomator.launcher;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.stream.Stream;
public class SupportedLanguagesTest {
@DisplayName("test if resource bundle is localized")
@ParameterizedTest(name = "{0}")
@MethodSource("languageTags")
public void testResourceBundleExists(String tag) {
var locale = Locale.forLanguageTag(tag);
Assertions.assertNotEquals("und", locale.toLanguageTag(), "Undefined language tag");
var bundle = Assertions.assertDoesNotThrow(() -> ResourceBundle.getBundle("/i18n/strings", locale));
Assertions.assertEquals(locale, bundle.getLocale());
Assertions.assertFalse(bundle.keySet().isEmpty());
}
public static Stream<String> languageTags() {
return SupportedLanguages.LANGUAGAE_TAGS.stream() //
.filter(tag -> !"en".equals(tag)); // english uses the default bundle
}
}
@@ -31,8 +31,8 @@ public class LaunchBasedTriggeringPolicyTest {
triggered = policy.isTriggeringEvent(activeFile, event);
Assertions.assertFalse(triggered);
Mockito.verifyZeroInteractions(activeFile);
Mockito.verifyZeroInteractions(event);
Mockito.verifyNoInteractions(activeFile);
Mockito.verifyNoInteractions(event);
}
}
@@ -22,7 +22,6 @@ public class PasswordStrengthUtilTest {
}
@Test
@Disabled("waiting on upstream fix") // https://github.com/nulab/zxcvbn4j/issues/54
public void testIssue979() {
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");
@@ -1,5 +1,6 @@
package org.cryptomator.ui.controls;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
@@ -8,7 +9,6 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import javafx.application.Platform;
import java.awt.GraphicsEnvironment;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -18,13 +18,10 @@ public class SecurePasswordFieldTest {
@BeforeAll
public static void initJavaFx() throws InterruptedException {
Assumptions.assumeFalse(GraphicsEnvironment.isHeadless());
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
Platform.startup(latch::countDown);
if (!latch.await(5L, TimeUnit.SECONDS)) {
throw new ExceptionInInitializerError();
}
var javafxStarted = latch.await(5, TimeUnit.SECONDS);
Assumptions.assumeTrue(javafxStarted);
}
@Nested
@@ -7,17 +7,19 @@ import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import java.io.IOException;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.function.Predicate;
public class RecoveryKeyFactoryTest {
private WordEncoder wordEncoder = new WordEncoder();
private MasterkeyFileAccess masterkeyFileAccess = Mockito.mock(MasterkeyFileAccess.class);
private RecoveryKeyFactory inTest = new RecoveryKeyFactory(wordEncoder, masterkeyFileAccess);
private final WordEncoder wordEncoder = new WordEncoder();
private final MasterkeyFileAccess masterkeyFileAccess = Mockito.mock(MasterkeyFileAccess.class);
private final RecoveryKeyFactory inTest = new RecoveryKeyFactory(wordEncoder, masterkeyFileAccess);
@Test
@DisplayName("createRecoveryKey() creates 44 words")
@@ -76,4 +78,19 @@ public class RecoveryKeyFactoryTest {
Assertions.assertTrue(result);
}
@ParameterizedTest(name = "passing validation = {0}")
@DisplayName("validateRecoveryKey() with extended validation")
@ValueSource(booleans = {true, false})
public void testValidateValidateRecoveryKeyWithValidKey(boolean extendedValidationResult) {
Predicate<byte[]> validator = Mockito.mock(Predicate.class);
Mockito.doReturn(extendedValidationResult).when(validator).test(Mockito.any());
boolean result = inTest.validateRecoveryKey("""
pathway lift abuse plenty export texture gentleman landscape beyond ceiling around leaf cafe charity \
border breakdown victory surely computer cat linger restrict infer crowd live computer true written amazed \
investor boot depth left theory snow whereby terminal weekly reject happiness circuit partial cup ad \
""", validator);
Mockito.verify(validator).test(Mockito.any());
Assertions.assertEquals(extendedValidationResult, result);
}
}