diff --git a/README.md b/README.md index b90d29f..fd40c21 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Windows-specific implementations of [integrations-api](https://github.com/crypto This project uses the following JVM properties: * `cryptomator.integrationsWin.autoStartShellLinkName` - Name of the shell link, which is placed in the Windows startup folder to start application on user login -* `cryptomator.integrationsWin.keychainPaths` - Colon separated list of paths, which are checked for encrypted data +* `cryptomator.integrationsWin.keychainPaths` - List of file paths, which are checked for data encrypted with the Windows data protection api ## Building diff --git a/pom.xml b/pom.xml index dec9da5..d9941b8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.cryptomator integrations-win - 1.2.1 + 1.2.2 Cryptomator Integrations for Windows Provides optional Windows services used by Cryptomator diff --git a/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java b/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java index 44bb532..9e3e591 100644 --- a/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java +++ b/src/main/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccess.java @@ -39,7 +39,7 @@ /** * Windows implementation for the {@link KeychainAccessProvider} based on the data protection API. - * The storage locations to check for encrypted data can be set with the JVM property {@value KEYCHAIN_PATHS_PROPERTY} as a colon({@value PATH_LIST_SEP}) separated list of paths. + * The storage locations to check for encrypted data can be set with the JVM property {@value KEYCHAIN_PATHS_PROPERTY} with the paths seperated with the character defined in the JVM property path.separator. */ @Priority(1000) @OperatingSystem(OperatingSystem.Value.WINDOWS) @@ -47,7 +47,6 @@ public class WindowsProtectedKeychainAccess implements KeychainAccessProvider { private static final String KEYCHAIN_PATHS_PROPERTY = "cryptomator.integrationsWin.keychainPaths"; private static final Logger LOG = LoggerFactory.getLogger(WindowsProtectedKeychainAccess.class); - private static final String PATH_LIST_SEP = ":"; private static final Path USER_HOME_REL = Path.of("~"); private static final Path USER_HOME = Path.of(System.getProperty("user.home")); private static final Gson GSON = new GsonBuilder() // @@ -72,8 +71,13 @@ public WindowsProtectedKeychainAccess() { } private static List readKeychainPathsFromEnv() { - return Optional.ofNullable(System.getProperty(KEYCHAIN_PATHS_PROPERTY)) - .stream().flatMap(rawPaths -> Arrays.stream(rawPaths.split(PATH_LIST_SEP))) + var keychainPaths = System.getProperty(KEYCHAIN_PATHS_PROPERTY, ""); + return parsePaths(keychainPaths, System.getProperty("path.separator")); + } + + // visible for testing + static List parsePaths(String listOfPaths, String pathSeparator) { + return Arrays.stream(listOfPaths.split(pathSeparator)) .filter(Predicate.not(String::isEmpty)) .map(Path::of) .map(WindowsProtectedKeychainAccess::resolveHomeDir) diff --git a/src/test/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccessTest.java b/src/test/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccessTest.java index a7c19e3..60ce399 100644 --- a/src/test/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccessTest.java +++ b/src/test/java/org/cryptomator/windows/keychain/WindowsProtectedKeychainAccessTest.java @@ -8,6 +8,8 @@ import org.cryptomator.integrations.keychain.KeychainAccessException; 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.io.TempDir; import org.mockito.Mockito; @@ -46,4 +48,35 @@ public void testStoreAndLoad() throws KeychainAccessException { Assertions.assertNull(keychain.loadPassphrase("nonExistingPassword")); } + @Nested + public class ParsePaths { + @Test + @DisplayName("String is split with path separator") + public void testParsePaths() { + String paths = "C:\\foo\\bar;bar\\kuz"; + var result = WindowsProtectedKeychainAccess.parsePaths(paths, ";"); + Assertions.assertEquals(2, result.size()); + Assertions.assertTrue(result.contains(Path.of("C:\\foo\\bar"))); + Assertions.assertTrue(result.contains(Path.of("bar\\kuz"))); + } + + @Test + @DisplayName("Empty string returns empty list") + public void testParsePathsEmpty() { + var result = WindowsProtectedKeychainAccess.parsePaths("", ";"); + Assertions.assertEquals(0, result.size()); + } + + @Test + @DisplayName("Strings starting with ~ are resolved to user home") + public void testParsePathsUserHome() { + var userHome = Path.of(System.getProperty("user.home")); + var result = WindowsProtectedKeychainAccess.parsePaths("this\\~\\not;~\\foo\\bar", ";"); + Assertions.assertEquals(2, result.size()); + Assertions.assertTrue(result.contains(Path.of("this\\~\\not"))); + Assertions.assertTrue(result.contains(userHome.resolve("foo\\bar"))); + } + + } + }