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")));
+ }
+
+ }
+
}