diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..5a718df
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,73 @@
+name: Build
+
+on:
+ [push]
+
+jobs:
+ build:
+ name: Build and Test
+ runs-on: ubuntu-latest
+ outputs:
+ artifact-version: ${{ steps.setversion.outputs.version }}
+ env:
+ BUILD_VERSION: SNAPSHOT
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-java@v1
+ with:
+ java-version: 14
+ - uses: actions/cache@v1
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: Ensure to use tagged version
+ run: mvn versions:set --file ./pom.xml -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags'
+ if: startsWith(github.ref, 'refs/tags/')
+ - name: Export the project version to the job environment and fix it as an ouput of this job
+ id: setversion
+ run: |
+ v=$(mvn help:evaluate "-Dexpression=project.version" -q -DforceStdout)
+ echo "::set-env name=BUILD_VERSION::${v}"
+ echo "::set-output name=version::${v}"
+ - name: Build and Test
+ run: mvn -B install
+ - name: Upload snapshot artifact cryptomator-cli-${{ env.BUILD_VERSION }}.jar
+ uses: actions/upload-artifact@v2
+ with:
+ name: cryptomator-cli-${{ env.BUILD_VERSION }}.jar
+ path: target/cryptomator-cli-*.jar
+
+ release:
+ name: Draft a Release on GitHub Releases and uploads the build artifacts to it
+ runs-on: ubuntu-latest
+ needs: build
+ if: startsWith(github.ref, 'refs/tags/')
+ steps:
+ - name: Download cryptomator-cli.jar
+ uses: actions/download-artifact@v1
+ with:
+ name: cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar
+ path: .
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: ${{ github.ref }}
+ body: |
+ :construction: Work in Progress
+ draft: true
+ prerelease: false
+ - name: Upload cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar to GitHub Releases
+ uses: actions/upload-release-asset@v1.0.1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar
+ asset_name: cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar
+ asset_content_type: application/jar
diff --git a/.gitignore b/.gitignore
index 5ddd9fc..3ef5c20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,5 @@ test-output/
out/
.idea_modules/
*.iws
+
+*.iml
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index f862116..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: java
-sudo: false
-jdk:
-- oraclejdk8
-install:
-- mvn dependency:go-offline
-script:
-- mvn clean install
-cache:
- directories:
- - "$HOME/.m2"
-deploy:
-- provider: releases
- api_key:
- secure: wclnmySSl1rMEWn5oe2cB1YamgmubDJxLo3vaQtpyKAVqdN6sy0VKa2/UjucxZY2mHJKQjWSjhpXSpROH2Ufp0RoC9wsBaICa+dEDcFO8dxxsczuOHsJzTr8ewfaIMzaPVxltOtyhEQcA+u5oX5l8aWFHpqYD8V86NKo5l0PF9wGTrjSksbzDq44lmU/dzjC1VaDjLTIjaGsqYQf6b3SXD8k3Loo1vlp/OFAryvOSZU9/53b7KfpAx7FckYZHLPOEPQDUIpKr5xTr2dRATaHXxT/9xqiIZz7kHOGDQYLo3lszdQdU+N8oL50RrPvkBtEFHIALv66CclTlP+Z6FoSLAgv13u+b2XpdmG/VeI1MM4tdW9fDPAQ1AQrH5nqiM4sKOLeViPtXYuKwH8TvNsTIA1Jl6QqjfmbEEGDefWUiRcc/g/O6Rxz3hjBB5lDvyKpT8nfuk0j1qYlLPjTuaGGRP5Ajjw+rGx4nTfA38Me5dldZ67bmbuBVDEKvSctTF86nntQm0Xf7vSUkd2eRZGT0Tj4SzeLVSroqFMOhlYqbNz4Zc67QwtRAjd3y5+6HqjwiVzzJM747PNFpy5jPee71z/Sdw/RtIGv0TroJmSwoIlJjbRYiYXT176xKZtzrOAUHwhDytzRqMi6DZj+ZSROOX+Rdwuc+lRRgvdFMDngMQI=
- file_glob: true
- file:
- - "target/cryptomator-cli-*.jar"
- skip_cleanup: true
- prerelease: true
- on:
- repo: cryptomator/cli
- branch: master
- tags: true
diff --git a/README.md b/README.md
index d111a44..fe24100 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
-[![Build Status](https://travis-ci.org/cryptomator/cli.svg?branch=develop)](https://travis-ci.org/cryptomator/cli)
+[![Build](https://github.com/cryptomator/cli/workflows/Build/badge.svg)](https://github.com/cryptomator/cli/actions?query=workflow%3ABuild)
+[![Latest Release](https://img.shields.io/github/release/cryptomator/cli/all.svg)](https://github.com/cryptomator/cli/releases/latest)
-# Cryptomator CLI version
+# Cryptomator CLI
-This is a minimal command line program which unlocks vaults, which can then be accessed via an embedded WebDAV server.
+This is a minimal command-line program that unlocks vaults which can then be accessed via an embedded WebDAV server.
## Disclaimer
@@ -10,18 +11,54 @@ This project is in an early stage and not ready for production use. We recommend
## Download and Usage
-Download the jar file via [GitHub Releases](https://github.com/cryptomator/cli/releases)
+Download the jar file via [GitHub Releases](https://github.com/cryptomator/cli/releases).
-Cryptomator CLI depends on a Java 8 JRE. In addition the JCE unlimited strength policy files (needed for 256-bit keys) must be installed.
+Cryptomator CLI requires that at least JDK 11 is present on your system.
```sh
java -jar cryptomator-cli-x.y.z.jar \
--vault demoVault=/path/to/vault --password demoVault=topSecret \
--vault otherVault=/path/to/differentVault --passwordfile otherVault=/path/to/fileWithPassword \
- --bind 0.0.0.0 --port 8080
+ --bind 127.0.0.1 --port 8080
# you can now mount http://localhost:8080/demoVault/
```
+Then you can access the vault using any WebDAV client.
+
+### Linux via davfs2
+
+First, you need to create a mount point for your vault
+
+```sh
+sudo mkdir /media/your/mounted/folder
+```
+
+Then you can mount the vault
+
+```sh
+sudo mount -t davfs http://localhost:8080/demoVault/ /media/your/mounted/folder
+```
+
+To unmount the vault, run
+
+```sh
+sudo umount /media/your/mounted/folder
+```
+
+### macOS via AppleScript
+
+Mount the vault with
+
+```sh
+osascript -e 'mount volume "http://localhost:8080/demoVault/"'
+```
+
+Unmount the vault with
+
+```sh
+osascript -e 'tell application "Finder" to if "demoVault" exists then eject "demoVault"'
+```
+
## License
This project is dual-licensed under the AGPLv3 for FOSS projects as well as a commercial license derived from the LGPL for independent software vendors and resellers. If you want to use this library in applications, that are *not* licensed under the AGPL, feel free to contact our [support team](https://cryptomator.org/help/).
diff --git a/pom.xml b/pom.xml
index 7de11e6..d0f1f12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,18 +2,18 @@
4.0.0
org.cryptomator
cli
- 0.3.1
+ 0.4.0
Cryptomator CLI
Command line program to access encrypted files via WebDAV.
https://github.com/cryptomator/cli
- 1.4.0
- 0.6.2
- 1.3.1
- 1.2.2
+ 1.9.10
+ 1.0.11
+ 1.4
+ 1.2.3
- 1.8
+ 11
UTF-8
@@ -71,17 +71,18 @@
maven-compiler-plugin
- 3.6.1
+ 3.8.1
${java.version}
+ ${java.version}
true
maven-assembly-plugin
- 3.0.0
+ 3.3.0
make-assembly
diff --git a/src/main/java/org/cryptomator/cli/Args.java b/src/main/java/org/cryptomator/cli/Args.java
index c87b0cf..13de342 100644
--- a/src/main/java/org/cryptomator/cli/Args.java
+++ b/src/main/java/org/cryptomator/cli/Args.java
@@ -8,14 +8,12 @@
*******************************************************************************/
package org.cryptomator.cli;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
@@ -23,6 +21,10 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
+import org.cryptomator.cli.pwd.PasswordFromFileStrategy;
+import org.cryptomator.cli.pwd.PasswordFromStdInputStrategy;
+import org.cryptomator.cli.pwd.PasswordStrategy;
+import org.cryptomator.cli.pwd.PasswordFromPropertyStrategy;
/**
* Parses program arguments. Does not validate them.
@@ -76,10 +78,7 @@ public class Args {
private final Properties vaultPaths;
private final Properties vaultPasswords;
private final Properties vaultPasswordFiles;
-
- private boolean hasPasswordOrPasswordFile(Object vaultPath) {
- return vaultPasswords.containsKey(vaultPath) || vaultPasswordFiles.containsKey(vaultPath);
- }
+ private final Map passwordStrategies;
public Args(CommandLine commandLine) throws ParseException {
this.bindAddr = commandLine.getOptionValue("bind", "localhost");
@@ -87,6 +86,7 @@ public Args(CommandLine commandLine) throws ParseException {
this.vaultPaths = commandLine.getOptionProperties("vault");
this.vaultPasswords = commandLine.getOptionProperties("password");
this.vaultPasswordFiles = commandLine.getOptionProperties("passwordfile");
+ this.passwordStrategies = new HashMap<>();
}
public String getBindAddr() {
@@ -98,32 +98,13 @@ public int getPort() {
}
public Set getVaultNames() {
- return vaultPaths.keySet().stream().filter(this::hasPasswordOrPasswordFile).map(String.class::cast).collect(Collectors.toSet());
+ return vaultPaths.keySet().stream().map(String.class::cast).collect(Collectors.toSet());
}
public String getVaultPath(String vaultName) {
return vaultPaths.getProperty(vaultName);
}
- public String getVaultPasswordPath(String vaultName) {
- return vaultPasswordFiles.getProperty(vaultName);
- }
-
- public String getVaultPassword(String vaultName) {
- if (vaultPasswords.getProperty(vaultName) == null) {
- Path vaultPasswordPath = Paths.get(vaultPasswordFiles.getProperty(vaultName));
- if (Files.isReadable(vaultPasswordPath) && Files.isRegularFile(vaultPasswordPath)) {
- try (Stream lines = Files.lines(vaultPasswordPath)) {
- return lines.findFirst().get().toString();
- } catch (IOException e) {
- return null;
- }
- }
- return null;
- }
- return vaultPasswords.getProperty(vaultName);
- }
-
public static Args parse(String[] arguments) throws ParseException {
CommandLine commandLine = new DefaultParser().parse(OPTIONS, arguments);
return new Args(commandLine);
@@ -133,4 +114,26 @@ public static void printUsage() {
new HelpFormatter().printHelp(USAGE, OPTIONS);
}
+ public PasswordStrategy addPasswortStrategy(final String vaultName) {
+ PasswordStrategy passwordStrategy = new PasswordFromStdInputStrategy(vaultName);
+
+ if (vaultPasswords.getProperty(vaultName) != null) {
+ passwordStrategy = new PasswordFromPropertyStrategy(
+ vaultName,
+ vaultPasswords.getProperty(vaultName)
+ );
+ } else if (vaultPasswordFiles.getProperty(vaultName) != null) {
+ passwordStrategy = new PasswordFromFileStrategy(
+ vaultName,
+ Paths.get(vaultPasswordFiles.getProperty(vaultName))
+ );
+ }
+
+ this.passwordStrategies.put(vaultName, passwordStrategy);
+ return passwordStrategy;
+ }
+
+ public PasswordStrategy getPasswordStrategy(final String vaultName) {
+ return passwordStrategies.get(vaultName);
+ }
}
diff --git a/src/main/java/org/cryptomator/cli/CryptomatorCli.java b/src/main/java/org/cryptomator/cli/CryptomatorCli.java
index 76ec3df..ab7940b 100644
--- a/src/main/java/org/cryptomator/cli/CryptomatorCli.java
+++ b/src/main/java/org/cryptomator/cli/CryptomatorCli.java
@@ -12,6 +12,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Set;
import org.apache.commons.cli.ParseException;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
@@ -40,22 +41,21 @@ public static void main(String[] rawArgs) throws IOException {
}
private static void validate(Args args) throws IllegalArgumentException {
+ Set vaultNames = args.getVaultNames();
if (args.getPort() < 0 || args.getPort() > 65536) {
throw new IllegalArgumentException("Invalid WebDAV Port.");
}
- if (args.getVaultNames().size() == 0) {
+ if (vaultNames.size() == 0) {
throw new IllegalArgumentException("No vault specified.");
}
- for (String vaultName : args.getVaultNames()) {
+ for (String vaultName : vaultNames) {
Path vaultPath = Paths.get(args.getVaultPath(vaultName));
- if ((args.getVaultPasswordPath(vaultName) != null) && args.getVaultPassword(vaultName) == null) {
- throw new IllegalArgumentException("Cannot read password from file: " + Paths.get(args.getVaultPasswordPath(vaultName)));
- }
if (!Files.isDirectory(vaultPath)) {
throw new IllegalArgumentException("Not a directory: " + vaultPath);
}
+ args.addPasswortStrategy(vaultName).validate();
}
}
@@ -67,7 +67,7 @@ private static void startup(Args args) throws IOException {
for (String vaultName : args.getVaultNames()) {
Path vaultPath = Paths.get(args.getVaultPath(vaultName));
LOG.info("Unlocking vault \"{}\" located at {}", vaultName, vaultPath);
- String vaultPassword = args.getVaultPassword(vaultName);
+ String vaultPassword = args.getPasswordStrategy(vaultName).password();
CryptoFileSystemProperties properties = CryptoFileSystemProperties.cryptoFileSystemProperties().withPassphrase(vaultPassword).build();
Path vaultRoot = CryptoFileSystemProvider.newFileSystem(vaultPath, properties).getPath("/");
WebDavServletController servlet = server.createWebDavServlet(vaultRoot, vaultName);
diff --git a/src/main/java/org/cryptomator/cli/pwd/PasswordFromFileStrategy.java b/src/main/java/org/cryptomator/cli/pwd/PasswordFromFileStrategy.java
new file mode 100644
index 0000000..cd20261
--- /dev/null
+++ b/src/main/java/org/cryptomator/cli/pwd/PasswordFromFileStrategy.java
@@ -0,0 +1,43 @@
+package org.cryptomator.cli.pwd;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+public class PasswordFromFileStrategy implements PasswordStrategy {
+ private static final Logger LOG = LoggerFactory.getLogger(PasswordFromFileStrategy.class);
+
+ private final String vaultName;
+ private final Path pathToFile;
+
+ public PasswordFromFileStrategy(final String vaultName, final Path pathToFile) {
+ this.vaultName = vaultName;
+ this.pathToFile = pathToFile;
+ }
+
+ @Override
+ public String password() {
+ LOG.info("Vault " + "'" + vaultName + "'" + " password from file.");
+
+ if (Files.isReadable(pathToFile) && Files.isRegularFile(pathToFile)) {
+ try (Stream lines = Files.lines(pathToFile)) {
+ return lines.findFirst().get().toString();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void validate() throws IllegalArgumentException {
+ if (!Files.isReadable(pathToFile)) {
+ throw new IllegalArgumentException("Cannot read password from file: " + pathToFile);
+ }
+ }
+
+}
diff --git a/src/main/java/org/cryptomator/cli/pwd/PasswordFromPropertyStrategy.java b/src/main/java/org/cryptomator/cli/pwd/PasswordFromPropertyStrategy.java
new file mode 100644
index 0000000..8d5909b
--- /dev/null
+++ b/src/main/java/org/cryptomator/cli/pwd/PasswordFromPropertyStrategy.java
@@ -0,0 +1,29 @@
+package org.cryptomator.cli.pwd;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PasswordFromPropertyStrategy implements PasswordStrategy {
+ private static final Logger LOG = LoggerFactory.getLogger(PasswordFromPropertyStrategy.class);
+
+ private final String vaultName;
+ private final String password;
+
+ public PasswordFromPropertyStrategy(final String vaultName, final String password) {
+ this.vaultName = vaultName;
+ this.password = password;
+ }
+
+ @Override
+ public String password() {
+ LOG.info("Vault " + "'" + vaultName + "'" + " password from property.");
+ return this.password;
+ }
+
+ @Override
+ public void validate() throws IllegalArgumentException {
+ if (password.equals("")) {
+ throw new IllegalArgumentException("Invalid password");
+ }
+ }
+}
diff --git a/src/main/java/org/cryptomator/cli/pwd/PasswordFromStdInputStrategy.java b/src/main/java/org/cryptomator/cli/pwd/PasswordFromStdInputStrategy.java
new file mode 100644
index 0000000..e374e7f
--- /dev/null
+++ b/src/main/java/org/cryptomator/cli/pwd/PasswordFromStdInputStrategy.java
@@ -0,0 +1,53 @@
+package org.cryptomator.cli.pwd;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.Console;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class PasswordFromStdInputStrategy implements PasswordStrategy {
+ private static final Logger LOG = LoggerFactory.getLogger(PasswordFromStdInputStrategy.class);
+
+ private final String vaultName;
+ private final String inputMessage = "Enter password for vault '%s': ";
+
+ public PasswordFromStdInputStrategy(final String vaultName) {
+ this.vaultName = vaultName;
+ }
+
+ @Override
+ public String password() {
+ LOG.info("Vault " + "'" + vaultName + "'" + " password from standard input.");
+
+ String password = "";
+ Console console = System.console();
+ if (console == null) {
+ LOG.warn("No console: non-interactive mode, instead use insecure replacement, PW is shown!");
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ System.out.println(String.format(inputMessage, vaultName));
+
+ try {
+ password = reader.readLine();
+ } catch (IOException e) {
+ LOG.error("There was an error reading line from console.");
+ e.printStackTrace();
+ }
+ } else {
+ System.out.println(String.format(inputMessage, vaultName));
+ password = new String(console.readPassword());
+ }
+
+ return password;
+ }
+
+ @Override
+ public void validate() throws IllegalArgumentException {
+ if (vaultName.equals("")) {
+ throw new IllegalArgumentException("Invalid vault name");
+ }
+ }
+}
diff --git a/src/main/java/org/cryptomator/cli/pwd/PasswordStrategy.java b/src/main/java/org/cryptomator/cli/pwd/PasswordStrategy.java
new file mode 100644
index 0000000..adf7c89
--- /dev/null
+++ b/src/main/java/org/cryptomator/cli/pwd/PasswordStrategy.java
@@ -0,0 +1,6 @@
+package org.cryptomator.cli.pwd;
+
+public interface PasswordStrategy {
+ String password();
+ void validate() throws IllegalArgumentException;
+}