diff --git a/pom.xml b/pom.xml index 64fb9657..425ff534 100644 --- a/pom.xml +++ b/pom.xml @@ -15,18 +15,24 @@ UTF-8 + 16 - 2.0.0-beta7 - 3.12.0 - 2.31 - 30.1-jre + 2.0.0-rc1 + 3.15.0 + 2.35.1 + 30.1.1-jre 1.7.30 - 5.7.0 - 3.7.7 + 5.7.1 + 3.9.0 2.2 + + + 6.1.6 + 0.8.6 + 1.6.8 @@ -121,7 +127,6 @@ maven-compiler-plugin 3.8.1 - 16 true @@ -219,7 +224,7 @@ org.owasp dependency-check-maven - 6.1.0 + ${dependency-check.version} 24 0 @@ -246,7 +251,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + ${jacoco.version} prepare-agent @@ -307,7 +312,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + ${nexus-staging.version} true ossrh diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java index 4a401c84..19d5be63 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java @@ -156,23 +156,21 @@ public static void initialize(Path pathToVault, CryptoFileSystemProperties prope } finally { Arrays.fill(rawKey, (byte) 0x00); } - assert containsVault(pathToVault, properties.vaultConfigFilename(), properties.masterkeyFilename()); + assert checkDirStructureForVault(pathToVault, properties.vaultConfigFilename(), properties.masterkeyFilename()) == DirStructure.VAULT; } /** - * Checks if the folder represented by the given path exists and contains a valid vault structure. + * Delegate to {@link DirStructure#checkDirStructure(Path, String, String)}. * - * @param pathToVault A directory path - * @param vaultConfigFilename Name of the vault config file - * @param masterkeyFilename Name of the masterkey file - * @return true if the directory seems to contain a vault. + * @param pathToAssumedVault + * @param vaultConfigFilename + * @param masterkeyFilename + * @return a {@link DirStructure} object + * @throws IOException * @since 2.0.0 */ - public static boolean containsVault(Path pathToVault, String vaultConfigFilename, String masterkeyFilename) { - Path vaultConfigPath = pathToVault.resolve(vaultConfigFilename); - Path masterkeyPath = pathToVault.resolve(masterkeyFilename); - Path dataDirPath = pathToVault.resolve(Constants.DATA_DIR_NAME); - return (Files.isReadable(vaultConfigPath) || Files.isReadable(masterkeyPath)) && Files.isDirectory(dataDirPath); + public static DirStructure checkDirStructureForVault(Path pathToAssumedVault, String vaultConfigFilename, String masterkeyFilename) throws IOException { + return DirStructure.checkDirStructure(pathToAssumedVault, vaultConfigFilename, masterkeyFilename); } /** diff --git a/src/main/java/org/cryptomator/cryptofs/DirStructure.java b/src/main/java/org/cryptomator/cryptofs/DirStructure.java new file mode 100644 index 00000000..daff7fb4 --- /dev/null +++ b/src/main/java/org/cryptomator/cryptofs/DirStructure.java @@ -0,0 +1,69 @@ +package org.cryptomator.cryptofs; + +import org.cryptomator.cryptofs.common.Constants; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; + +/** + * Enumeration of the vault directory structure resemblances. + *

+ * A valid vault must contain a `d` directory. + * Beginning with vault format 8, it must also contain a vault config file. + * If the vault format is lower than 8, it must instead contain a masterkey file. + *

+ * In the latter case, to distinguish between a damaged vault 8 directory and a legacy vault the masterkey file must be read. + * For efficiency reasons, this class only checks for existence/readability of the above elements. + * Hence, if the result of {@link #checkDirStructure(Path, String, String)} is {@link #MAYBE_LEGACY}, one needs to parse + * the masterkey file and read out the vault version to determine this case. + * + * @since 2.0.0 + */ +public enum DirStructure { + + /** + * Dir contains a d dir as well as a vault config file. + */ + VAULT, + + /** + * Dir contains a d dir and a masterkey file, but misses a vault config file. + * Either needs migration to a newer format or damaged. + */ + MAYBE_LEGACY, + + /** + * Dir does not qualify as vault. + */ + UNRELATED; + + + /** + * Analyzes the structure of the given directory under certain vault existence criteria. + * + * @param pathToVault A directory path + * @param vaultConfigFilename Name of the vault config file + * @param masterkeyFilename Name of the masterkey file + * @return enum indicating what this directory might be + * @throws IOException if the provided path is not a directory, does not exist or cannot be read + */ + public static DirStructure checkDirStructure(Path pathToVault, String vaultConfigFilename, String masterkeyFilename) throws IOException { + if(! Files.readAttributes(pathToVault, BasicFileAttributes.class).isDirectory()) { + throw new NotDirectoryException(pathToVault.toString()); + } + Path vaultConfigPath = pathToVault.resolve(vaultConfigFilename); + Path masterkeyPath = pathToVault.resolve(masterkeyFilename); + Path dataDirPath = pathToVault.resolve(Constants.DATA_DIR_NAME); + if (Files.isDirectory(dataDirPath)) { + if (Files.isReadable(vaultConfigPath)) { + return VAULT; + } else if (Files.isReadable(masterkeyPath)) { + return MAYBE_LEGACY; + } + } + return UNRELATED; + } +} diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java index 5ae7a36a..6ae7b903 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java @@ -227,9 +227,9 @@ public void initializeVaults() { @Test @Order(2) @DisplayName("get filesystem with incorrect credentials") - public void testGetFsWithWrongCredentials() { - Assumptions.assumeTrue(CryptoFileSystemProvider.containsVault(pathToVault1, "vault.cryptomator", "masterkey.cryptomator")); - Assumptions.assumeTrue(CryptoFileSystemProvider.containsVault(pathToVault2, "vault.cryptomator", "masterkey.cryptomator")); + public void testGetFsWithWrongCredentials() throws IOException { + Assumptions.assumeTrue(CryptoFileSystemProvider.checkDirStructureForVault(pathToVault1, "vault.cryptomator", "masterkey.cryptomator") == DirStructure.VAULT); + Assumptions.assumeTrue(CryptoFileSystemProvider.checkDirStructureForVault(pathToVault2, "vault.cryptomator", "masterkey.cryptomator") == DirStructure.VAULT); Assertions.assertAll( () -> { URI fsUri = CryptoFileSystemUri.create(pathToVault1); diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java index d786d9d0..64836e78 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java @@ -46,7 +46,6 @@ import static java.nio.file.StandardOpenOption.APPEND; import static java.util.Arrays.asList; import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemProperties; -import static org.cryptomator.cryptofs.CryptoFileSystemProvider.containsVault; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.mockito.Mockito.mock; @@ -211,69 +210,6 @@ public void testNewFileSystem() throws IOException, MasterkeyLoadingFailedExcept Mockito.verify(fileSystems).create(Mockito.same(inTest), Mockito.eq(pathToVault.toAbsolutePath()), Mockito.eq(properties)); } - @Test - public void testContainsVaultReturnsTrueIfDirectoryContainsVaultConfigFileAndDataDir() throws IOException { - FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); - - String vaultConfigFilename = "vaultconfig.foo.baz"; - String masterkeyFilename = "masterkey.foo.baz"; - Path pathToVault = fs.getPath("/vaultDir"); - - Path vaultConfigFile = pathToVault.resolve(vaultConfigFilename); - Path dataDir = pathToVault.resolve("d"); - Files.createDirectories(dataDir); - Files.write(vaultConfigFile, new byte[0]); - - Assertions.assertTrue(containsVault(pathToVault, vaultConfigFilename, masterkeyFilename)); - } - - @Test - public void testContainsVaultReturnsTrueIfDirectoryContainsMasterkeyFileAndDataDir() throws IOException { - FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); - - String vaultConfigFilename = "vaultconfig.foo.baz"; - String masterkeyFilename = "masterkey.foo.baz"; - Path pathToVault = fs.getPath("/vaultDir"); - - Path masterkeyFile = pathToVault.resolve(masterkeyFilename); - Path dataDir = pathToVault.resolve("d"); - Files.createDirectories(dataDir); - Files.write(masterkeyFile, new byte[0]); - - Assertions.assertTrue(containsVault(pathToVault, vaultConfigFilename, masterkeyFilename)); - } - - @Test - public void testContainsVaultReturnsFalseIfDirectoryContainsOnlyDataDir() throws IOException { - FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); - - String vaultConfigFilename = "vaultconfig.foo.baz"; - String masterkeyFilename = "masterkey.foo.baz"; - Path pathToVault = fs.getPath("/vaultDir"); - - Path dataDir = pathToVault.resolve("d"); - Files.createDirectories(dataDir); - - Assertions.assertFalse(containsVault(pathToVault, vaultConfigFilename, masterkeyFilename)); - } - - @Test - public void testContainsVaultReturnsFalseIfDirectoryContainsNoDataDir() throws IOException { - FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); - - String vaultConfigFilename = "vaultconfig.foo.baz"; - String masterkeyFilename = "masterkey.foo.baz"; - Path pathToVault = fs.getPath("/vaultDir"); - - Path vaultConfigFile = pathToVault.resolve(vaultConfigFilename); - Path masterkeyFile = pathToVault.resolve(masterkeyFilename); - Files.createDirectories(pathToVault); - Files.write(vaultConfigFile, new byte[0]); - Files.write(masterkeyFile, new byte[0]); - - Assertions.assertFalse(containsVault(pathToVault, vaultConfigFilename, masterkeyFilename)); - } - @Test public void testGetFileSystemInvokesFileSystemsGetWithPathToVaultFromUri() { Path pathToVault = get("a").toAbsolutePath(); diff --git a/src/test/java/org/cryptomator/cryptofs/DirStructureTest.java b/src/test/java/org/cryptomator/cryptofs/DirStructureTest.java new file mode 100644 index 00000000..f35818bc --- /dev/null +++ b/src/test/java/org/cryptomator/cryptofs/DirStructureTest.java @@ -0,0 +1,75 @@ +package org.cryptomator.cryptofs; + +import org.cryptomator.cryptofs.common.Constants; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +public class DirStructureTest { + + private static final String KEY = "key"; + private static final String CONFIG = "config"; + + @TempDir + Path vaultPath; + + @Test + public void testNonExistingVaultPathThrowsIOException() { + Path vaultPath = Path.of("this/certainly/does/not/exist"); + Assumptions.assumeTrue(Files.notExists(vaultPath)); + + Assertions.assertThrows(IOException.class, () -> DirStructure.checkDirStructure(vaultPath, CONFIG, KEY)); + } + + @Test + public void testNonDirectoryVaultPathThrowsIOException() throws IOException { + Path tmp = vaultPath.resolve("this"); + Files.createFile(tmp); + Assumptions.assumeTrue(Files.exists(tmp)); + + Assertions.assertThrows(IOException.class, () -> DirStructure.checkDirStructure(tmp, CONFIG, KEY)); + } + + @ParameterizedTest(name = "Testing all combinations of data dir, config and masterkey file existence.") + @MethodSource("provideAllCases") + public void testAllCombosOfDataAndConfigAndKey(boolean createDataDir, boolean createConfig, boolean createKey, DirStructure expectedResult) throws IOException { + Path keyPath = vaultPath.resolve(KEY); + Path configPath = vaultPath.resolve(CONFIG); + Path dataDir = vaultPath.resolve(Constants.DATA_DIR_NAME); + + if (createDataDir) { + Files.createDirectory(dataDir); + } + if (createConfig) { + Files.createFile(configPath); + } + if (createKey) { + Files.createFile(keyPath); + } + + Assertions.assertEquals(expectedResult, DirStructure.checkDirStructure(vaultPath, CONFIG, KEY)); + } + + private static Stream provideAllCases() { + return Stream.of( + Arguments.of(true, true, true, DirStructure.VAULT), + Arguments.of(true, true, false, DirStructure.VAULT), + Arguments.of(true, false, true, DirStructure.MAYBE_LEGACY), + Arguments.of(true, false, false, DirStructure.UNRELATED), + Arguments.of(false, false, false, DirStructure.UNRELATED), + Arguments.of(false, false, true, DirStructure.UNRELATED), + Arguments.of(false, true, false, DirStructure.UNRELATED), + Arguments.of(false, true, true, DirStructure.UNRELATED) + ); + } + +}