diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..31b43f7cd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,131 @@ +name: Build + +on: + [push] + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 14 + uses: actions/setup-java@v1 + with: + java-version: 14 + - uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ github.run_id }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Ensure to use tagged version + run: mvn versions:set --file main/pom.xml -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags' + if: startsWith(github.ref, 'refs/tags/') + - name: Build with Maven + run: mvn -B install --file main/pom.xml -Pcoverage + - name: Run Codacy Coverage Reporter + run: | + curl -o ~/codacy-coverage-reporter.jar https://repo.maven.apache.org/maven2/com/codacy/codacy-coverage-reporter/7.1.0/codacy-coverage-reporter-7.1.0-assembly.jar + $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial + $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/keychain/target/site/jacoco/jacoco.xml --partial + $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial + $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial + $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar final + env: + CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} + + assemble-build-kit: + name: Assemble Build Kit + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 14 + uses: actions/setup-java@v1 + with: + java-version: 14 + - uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ github.run_id }} + - name: Ensure to use tagged version + run: mvn versions:set --file main/pom.xml -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags' + if: startsWith(github.ref, 'refs/tags/') + - name: Build with Maven + run: mvn -B package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease + - name: Upload buildkit-linux.zip + uses: actions/upload-artifact@v1 + with: + name: buildkit-linux.zip + path: main/buildkit/target/buildkit-linux.zip + - name: Upload buildkit-mac.zip + uses: actions/upload-artifact@v1 + with: + name: buildkit-mac.zip + path: main/buildkit/target/buildkit-mac.zip + - name: Upload buildkit-win.zip + uses: actions/upload-artifact@v1 + with: + name: buildkit-win.zip + path: main/buildkit/target/buildkit-win.zip + + github-release: + name: Draft a Release on GitHub Releases + runs-on: ubuntu-latest + needs: assemble-build-kit + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Download buildkit-linux.zip + uses: actions/download-artifact@v1 + with: + name: buildkit-linux.zip + path: . + - name: Download buildkit-mac.zip + uses: actions/download-artifact@v1 + with: + name: buildkit-mac.zip + path: . + - name: Download buildkit-win.zip + uses: actions/download-artifact@v1 + with: + name: buildkit-win.zip + path: . + - id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + body: | + TODO + draft: true + prerelease: true + - name: Upload buildkit-linux.zip 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: buildkit-linux.zip + asset_name: buildkit-linux.zip + asset_content_type: application/zip + - name: Upload buildkit-mac.zip 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: buildkit-mac.zip + asset_name: buildkit-mac.zip + asset_content_type: application/zip + - name: Upload buildkit-win.zip 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: buildkit-win.zip + asset_name: buildkit-win.zip + asset_content_type: application/zip \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 000000000..727c7b1bb --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index c20d5a34e..eab8a318e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/.travis-deploy-release.tmpl.json b/.travis-deploy-release.tmpl.json deleted file mode 100644 index 435e3dd82..000000000 --- a/.travis-deploy-release.tmpl.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "package": { - "name": "buildkit", - "repo": "cryptomator", - "subject": "cryptomator" - }, - "version": { - "name": "$TRAVIS_TAG", - "desc": "Cryptomator version $TRAVIS_TAG", - "released": "$TODAY", - "vcs_tag": "$TRAVIS_TAG", - "gpgSign": true - }, - "files": - [ - {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$TRAVIS_TAG/$1"} - ], - "publish": true -} diff --git a/.travis-deploy-snapshot.json b/.travis-deploy-snapshot.json deleted file mode 100644 index fa3897a4d..000000000 --- a/.travis-deploy-snapshot.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "package": { - "name": "buildkit", - "repo": "cryptomator", - "subject": "cryptomator" - }, - "version": { - "name": "snapshot" - }, - "files": - [ - {"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/snapshot/$1", "matrixParams": {"override": 1}} - ], - "publish": true -} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 41f912fa8..000000000 --- a/.travis.yml +++ /dev/null @@ -1,65 +0,0 @@ -dist: bionic -language: java -sudo: false -jdk: -- openjdk11 -cache: - directories: - - $HOME/.m2 -env: - global: - - secure: "HftEaabMmWn5GwKFKksUkOcelc3Mn7xazwAEy+4d4gL1+F8VhID/6DCK7nas+afUymWnxTano8Rv4Ci5MWryNkNkTH+FUPWmF3xWezc3hajSyS7RB92IZ8VPetl4Fo8UI1WwM5apDEaugalPxkIf8a7N+lpG5X/Gpumwzo3Be3w=" # BINTRAY_API_KEY - - secure: "oWFgRTVP6lyTa7qVxlvkpm20MtVc3BtmsNXQJS6bfg2A0o/iCQMNx7OD59BaafCLGRKvCcJVESiC8FlSylVMS7CDSyYu0gg70NUiIuHp4NBM5inFWYCy/PdQsCTzr5uvNG+rMFQpMFRaCV0FrfM3tLondcVkhsHL68l93Xoexx4=" # CODACY_PROJECT_TOKEN - - secure: "zJxgytA2Ks5Xzv+7kUaUq+EBFNQw9Qec63lcMJVuXVWczjL16nKW1EzzV515ag+OWL46z3lEPForDhufw0VtFnNmaX68jkO0mp01eLrHApc1llN2Y/U8GBXfNNazN4+Kom4H+z/AO+wJr8EsKMMUczCdQ3APgd9uVI0hzXw/Z3M=" # GITHUB_API_KEY -addons: - apt: - packages: - - haveged -install: -# - curl -o $HOME/.m2/settings.xml https://gist.githubusercontent.com/cryptobot/cf5fbd909c4782aaeeeb7c7f4a1a43da/raw/e60ee486e34ee0c79f89f947abe2c83b4290c6bb/settings.xml -- mvn -fmain/pom.xml clean install -DskipTests -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN org.codehaus.mojo:versions-maven-plugin:help dependency:go-offline -Pcoverage,release # "clean install" needed until we can exclude artifacts currently in the reactor, see https://maven.apache.org/plugins/maven-dependency-plugin/go-offline-mojo.html#excludeReactor and https://issues.apache.org/jira/browse/MDEP-568 -before_script: -- | - if [[ -n "$TRAVIS_TAG" ]]; then - mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=$TRAVIS_TAG - else - mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=SNAPSHOT-$(echo $TRAVIS_COMMIT | head -c7) - fi -script: -- mvn --update-snapshots -fmain/pom.xml clean test verify -Pcoverage -after_success: -- curl -o ~/codacy-coverage-reporter.jar https://repo.maven.apache.org/maven2/com/codacy/codacy-coverage-reporter/7.1.0/codacy-coverage-reporter-7.1.0-assembly.jar -- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial -- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/keychain/target/site/jacoco/jacoco.xml --partial -- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial -- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial -- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar final -before_deploy: -- mvn -fmain/pom.xml package -Prelease -DskipTests -- export TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json -deploy: -- provider: bintray # SNAPSHOTS - file: .travis-deploy-snapshot.json - user: cryptobot - key: $BINTRAY_API_KEY - skip_cleanup: true - on: - repo: cryptomator/cryptomator - branch: develop -- provider: bintray # RELEASES - file: .travis-deploy-release.json - user: cryptobot - key: $BINTRAY_API_KEY - skip_cleanup: true - on: - repo: cryptomator/cryptomator - tags: true -- provider: releases - api_key: $GITHUB_API_KEY - file_glob: true - file: - - "main/buildkit/target/buildkit-*.zip" - skip_cleanup: true - on: - repo: cryptomator/cryptomator - tags: true \ No newline at end of file diff --git a/README.md b/README.md index 2f8c79a3d..a8a7248cd 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ Cryptomator is provided free of charge as an open-source project despite the hig - [One-time or recurring donation via Cryptomator's website.](https://cryptomator.org/#donate) - [Become a sponsor via Cryptomator's sponsors website.](https://cryptomator.org/sponsors/) +### Gold Sponsors + +[gee-whiz](https://www.gee-whiz.de/) + ### Silver Sponsors [![TheBestVPN](https://cryptomator.org/img/sponsors/thebestvpn.png)](https://thebestvpn.com/) @@ -55,7 +59,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator ### Security Architecture -For more information on the security details visit [cryptomator.org](https://cryptomator.org/architecture/). +For more information on the security details visit [cryptomator.org](https://docs.cryptomator.org/en/latest/security/architecture/). ## Building diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml index b763fdd2c..b29cb977d 100644 --- a/main/buildkit/pom.xml +++ b/main/buildkit/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.5.0 + 1.5.1 buildkit pom diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 2e7e88a2a..fd6b0aea0 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.5.0 + 1.5.1 commons Cryptomator Commons diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java index 7bb7aefa0..390e40f5a 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java @@ -10,7 +10,9 @@ package org.cryptomator.common.settings; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; import org.cryptomator.common.Environment; import org.cryptomator.common.LazyInitializer; import org.slf4j.Logger; @@ -77,12 +79,15 @@ public class SettingsProvider implements Supplier { LOG.debug("Attempting to load settings from {}", path); try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); // Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) { - Settings settings = gson.fromJson(reader, Settings.class); - if (settings == null) { - throw new IOException("Unexpected EOF"); + JsonElement json = JsonParser.parseReader(reader); + if (json.isJsonObject()) { + Settings settings = gson.fromJson(json, Settings.class); + LOG.info("Settings loaded from {}", path); + return Stream.of(settings); + } else { + LOG.warn("Invalid json file {}", path); + return Stream.empty(); } - LOG.info("Settings loaded from {}", path); - return Stream.of(settings); } catch (NoSuchFileException e) { return Stream.empty(); } catch (IOException e) { diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java index 6e35ffab2..e9e0fbd6d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -27,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Provider; import java.io.IOException; import java.nio.file.NoSuchFileException; @@ -51,6 +52,7 @@ public class Vault { private final StringBinding defaultMountFlags; private final AtomicReference cryptoFileSystem; private final ObjectProperty state; + private final ObjectProperty lastKnownException; private final VaultStats stats; private final StringBinding displayableName; private final StringBinding displayablePath; @@ -59,18 +61,20 @@ public class Vault { private final BooleanBinding unlocked; private final BooleanBinding missing; private final BooleanBinding needsMigration; + private final BooleanBinding unknownError; private final StringBinding accessPoint; private final BooleanBinding accessPointPresent; private volatile Volume volume; @Inject - Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, ObjectProperty state, VaultStats stats) { + Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, ObjectProperty state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) { this.vaultSettings = vaultSettings; this.volumeProvider = volumeProvider; this.defaultMountFlags = defaultMountFlags; this.cryptoFileSystem = cryptoFileSystem; this.state = state; + this.lastKnownException = lastKnownException; this.stats = stats; this.displayableName = Bindings.createStringBinding(this::getDisplayableName, vaultSettings.path()); this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path()); @@ -79,6 +83,7 @@ public class Vault { this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state); this.missing = Bindings.createBooleanBinding(this::isMissing, state); this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state); + this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state); this.accessPoint = Bindings.createStringBinding(this::getAccessPoint, state); this.accessPointPresent = this.accessPoint.isNotEmpty(); } @@ -149,6 +154,18 @@ public class Vault { state.setValue(value); } + public ObjectProperty lastKnownExceptionProperty() { + return lastKnownException; + } + + public Exception getLastKnownException() { + return lastKnownException.get(); + } + + public void setLastKnownException(Exception e) { + lastKnownException.setValue(e); + } + public BooleanBinding lockedProperty() { return locked; } @@ -189,6 +206,14 @@ public class Vault { return state.get() == VaultState.NEEDS_MIGRATION; } + public BooleanBinding unknownErrorProperty() { + return unknownError; + } + + public boolean isUnknownError() { + return state.get() == VaultState.ERROR; + } + public StringBinding displayableNameProperty() { return displayableName; } diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java index 58abd2d37..98a9f951d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java @@ -10,6 +10,9 @@ import org.cryptomator.common.settings.VaultSettings; import dagger.Subcomponent; +import javax.annotation.Nullable; +import javax.inject.Named; + @PerVault @Subcomponent(modules = {VaultModule.class}) public interface VaultComponent { @@ -25,6 +28,9 @@ public interface VaultComponent { @BindsInstance Builder initialVaultState(VaultState vaultState); + @BindsInstance + Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause); + VaultComponent build(); } diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 521ca4658..09bd60c77 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -14,6 +14,8 @@ import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.migration.Migrators; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; @@ -29,7 +31,9 @@ import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; @Singleton public class VaultListManager { - + + private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class); + private final VaultComponent.Builder vaultComponentBuilder; private final ObservableList vaultList; @@ -37,7 +41,7 @@ public class VaultListManager { public VaultListManager(VaultComponent.Builder vaultComponentBuilder, Settings settings) { this.vaultComponentBuilder = vaultComponentBuilder; this.vaultList = FXCollections.observableArrayList(Vault::observables); - + addAll(settings.getDirectories()); vaultList.addListener(new VaultListChangeListener(settings.getDirectories())); } @@ -61,12 +65,12 @@ public class VaultListManager { return newVault; } } - + private void addAll(Collection vaultSettings) { Collection vaults = vaultSettings.stream().map(this::create).collect(Collectors.toList()); vaultList.addAll(vaults); } - + private Optional get(Path vaultPath) { return vaultList.stream().filter(v -> { try { @@ -78,22 +82,25 @@ public class VaultListManager { } private Vault create(VaultSettings vaultSettings) { - VaultState vaultState = determineVaultState(vaultSettings.path().get()); - VaultComponent comp = vaultComponentBuilder.vaultSettings(vaultSettings).initialVaultState(vaultState).build(); - return comp.vault(); + VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings); + try { + VaultState vaultState = determineVaultState(vaultSettings.path().get()); + compBuilder.initialVaultState(vaultState); + } catch (IOException e) { + LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e); + compBuilder.initialVaultState(VaultState.ERROR); + compBuilder.initialErrorCause(e); + } + return compBuilder.build().vault(); } - public static VaultState determineVaultState(Path pathToVault) { - try { - if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) { - return VaultState.MISSING; - } else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) { - return VaultState.NEEDS_MIGRATION; - } else { - return VaultState.LOCKED; - } - } catch (IOException e) { - return VaultState.ERROR; + public static VaultState determineVaultState(Path pathToVault) throws IOException { + if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) { + return VaultState.MISSING; + } else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) { + return VaultState.NEEDS_MIGRATION; + } else { + return VaultState.LOCKED; } } diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java index f9c6b9177..1afd9d88b 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java @@ -23,6 +23,8 @@ import org.cryptomator.cryptofs.CryptoFileSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import javax.inject.Named; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -46,6 +48,14 @@ public class VaultModule { return new SimpleObjectProperty<>(initialState); } + @Provides + @Named("lastKnownException") + @PerVault + public ObjectProperty provideLastKnownException(@Named("lastKnownException") @Nullable Exception initialErrorCause) { + return new SimpleObjectProperty<>(initialErrorCause); + } + + @Provides public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) { VolumeImpl preferredImpl = settings.preferredVolumeImpl().get(); diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java index 1d31e9cea..347ec1e45 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java @@ -7,6 +7,8 @@ package org.cryptomator.common.settings; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.io.IOException; @@ -34,4 +36,16 @@ public class SettingsJsonAdapterTest { Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get()); } + @ParameterizedTest(name = "fromJson() should throw IOException for input: {0}") + @ValueSource(strings = { + "", + "", + "{invalidjson}" + }) + public void testDeserializeMalformed(String input) { + Assertions.assertThrows(IOException.class, () -> { + adapter.fromJson(input); + }); + } + } diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml index ee87d5163..7b99f1730 100644 --- a/main/keychain/pom.xml +++ b/main/keychain/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.5.0 + 1.5.1 keychain System Keychain Access diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml index 962160c6f..ef3d74d4c 100644 --- a/main/launcher/pom.xml +++ b/main/launcher/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.5.0 + 1.5.1 launcher Cryptomator Launcher diff --git a/main/pom.xml b/main/pom.xml index 5d58a60ef..dbb5c18eb 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator main - 1.5.0 + 1.5.1 pom Cryptomator @@ -24,7 +24,7 @@ UTF-8 - 1.9.5 + 1.9.7 2.2.2 1.2.3 1.1.13 @@ -331,7 +331,7 @@ maven-compiler-plugin 3.8.1 - 11 + 14 com.google.dagger diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 2ced5b191..364d66d62 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.5.0 + 1.5.1 ui Cryptomator GUI diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java b/main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java index 9b272a585..27a51c00b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java @@ -21,10 +21,11 @@ abstract class ErrorModule { static FXMLLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) { return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle); } - + @Provides @Named("stackTrace") static String provideStackTrace(Throwable cause) { + // TODO deduplicate VaultDetailUnknownErrorController.java ByteArrayOutputStream baos = new ByteArrayOutputStream(); cause.printStackTrace(new PrintStream(baos)); return baos.toString(StandardCharsets.UTF_8); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java b/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java index d23951a4b..497913e65 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.controls; +import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.beans.property.StringProperty; import javafx.geometry.Pos; @@ -55,19 +56,24 @@ public class NiceSecurePasswordField extends StackPane { passwordField.revealPasswordProperty().bind(revealPasswordButton.selectedProperty()); getChildren().addAll(passwordField, iconContainer); + disabledProperty().addListener(this::disabledChanged); } private FontAwesome5Icon getRevealPasswordGlyph() { return revealPasswordButton.isSelected() ? FontAwesome5Icon.EYE_SLASH : FontAwesome5Icon.EYE; } + private void disabledChanged(@SuppressWarnings("unused") Observable observable) { + revealPasswordButton.setSelected(false); + } + /* Passthrough */ @Override public void requestFocus() { passwordField.requestFocus(); } - + public String getText() { return passwordField.getText(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java index 5043fa485..04a047424 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java @@ -6,13 +6,9 @@ import dagger.Provides; import dagger.multibindings.IntoMap; import javafx.scene.Scene; import javafx.scene.image.Image; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyCodeCombination; -import javafx.scene.input.KeyCombination; import javafx.stage.Stage; import javafx.stage.StageStyle; import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; -import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FXMLLoaderFactory; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxControllerKey; @@ -87,7 +83,7 @@ abstract class MainWindowModule { @IntoMap @FxControllerKey(VaultDetailController.class) abstract FxController bindVaultDetailController(VaultDetailController controller); - + @Binds @IntoMap @FxControllerKey(WelcomeController.class) @@ -102,7 +98,7 @@ abstract class MainWindowModule { @IntoMap @FxControllerKey(VaultDetailUnlockedController.class) abstract FxController bindVaultDetailUnlockedController(VaultDetailUnlockedController controller); - + @Binds @IntoMap @FxControllerKey(VaultDetailMissingVaultController.class) @@ -113,11 +109,15 @@ abstract class MainWindowModule { @FxControllerKey(VaultDetailNeedsMigrationController.class) abstract FxController bindVaultDetailNeedsMigrationController(VaultDetailNeedsMigrationController controller); + @Binds + @IntoMap + @FxControllerKey(VaultDetailUnknownErrorController.class) + abstract FxController bindVaultDetailUnknownErrorController(VaultDetailUnknownErrorController controller); + @Binds @IntoMap @FxControllerKey(VaultListCellController.class) abstract FxController bindVaultListCellController(VaultListCellController controller); - } diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java new file mode 100644 index 000000000..4c23ba4ae --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java @@ -0,0 +1,40 @@ +package org.cryptomator.ui.mainwindow; + +import javafx.beans.binding.Binding; +import javafx.beans.property.ObjectProperty; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.ui.common.FxController; +import org.fxmisc.easybind.EasyBind; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + +@MainWindowScoped +public class VaultDetailUnknownErrorController implements FxController { + + private final Binding stackTrace; + + @Inject + public VaultDetailUnknownErrorController(ObjectProperty vault) { + this.stackTrace = EasyBind.select(vault).selectObject(Vault::lastKnownExceptionProperty).map(this::provideStackTrace).orElse(""); + } + + private String provideStackTrace(Throwable cause) { + // TODO deduplicate ErrorModule.java + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + cause.printStackTrace(new PrintStream(baos)); + return baos.toString(StandardCharsets.UTF_8); + } + + /* Getter/Setter */ + + public Binding stackTraceProperty() { + return stackTrace; + } + + public String getStackTrace() { + return stackTrace.getValue(); + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index 167138e0f..625a10e65 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -18,6 +18,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.io.IOException; @MainWindowScoped public class VaultListController implements FxController { @@ -68,8 +69,14 @@ public class VaultListController implements FxController { case LOCKED: case NEEDS_MIGRATION: case MISSING: - VaultState determinedState = VaultListManager.determineVaultState(newValue.getPath()); - newValue.setState(determinedState); + try { + VaultState determinedState = VaultListManager.determineVaultState(newValue.getPath()); + newValue.setState(determinedState); + } catch (IOException e) { + LOG.warn("Failed to determine vault state for " + newValue.getPath(), e); + newValue.setState(VaultState.ERROR); + newValue.setLastKnownException(e); + } break; case ERROR: case UNLOCKED: diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java index 826fa6053..d7340618d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java @@ -99,6 +99,7 @@ public class MigrationRunController implements FxController { LOG.info("Migrating vault {}", vault.getPath()); CharSequence password = passwordField.getCharacters(); vault.setState(VaultState.PROCESSING); + passwordField.setDisable(true); ScheduledFuture progressSyncTask = scheduler.scheduleAtFixedRate(() -> { Platform.runLater(() -> { migrationProgress.set(volatileMigrationProgress); @@ -120,6 +121,7 @@ public class MigrationRunController implements FxController { } }).onError(InvalidPassphraseException.class, e -> { Animations.createShakeWindowAnimation(window).play(); + passwordField.setDisable(false); passwordField.selectAll(); passwordField.requestFocus(); vault.setState(VaultState.NEEDS_MIGRATION); @@ -133,6 +135,7 @@ public class MigrationRunController implements FxController { vault.setState(VaultState.NEEDS_MIGRATION); errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene(); }).andFinally(() -> { + passwordField.setDisable(false); progressSyncTask.cancel(true); }).runOnce(executor); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index f06a03621..d725fd0ea 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -4,7 +4,6 @@ import dagger.Lazy; import javafx.beans.binding.Bindings; import javafx.beans.binding.ObjectBinding; import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.concurrent.Task; @@ -30,7 +29,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.inject.Named; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.NotDirectoryException; import java.util.Arrays; @@ -90,9 +88,11 @@ public class UnlockController implements FxController { public void unlock() { LOG.trace("UnlockController.unlock()"); CharSequence password = passwordField.getCharacters(); - + Task task = vaultService.createUnlockTask(vault, password); + passwordField.setDisable(true); task.setOnSucceeded(event -> { + passwordField.setDisable(false); if (keychainAccess.isPresent() && savePassword.isSelected()) { try { keychainAccess.get().storePassphrase(vault.getId(), password); @@ -105,12 +105,12 @@ public class UnlockController implements FxController { window.setScene(successScene.get()); }); task.setOnFailed(event -> { + passwordField.setDisable(false); if (task.getException() instanceof InvalidPassphraseException) { Animations.createShakeWindowAnimation(window).play(); passwordField.selectAll(); passwordField.requestFocus(); - } else if (task.getException() instanceof NotDirectoryException - || task.getException() instanceof DirectoryNotEmptyException) { + } else if (task.getException() instanceof NotDirectoryException || task.getException() instanceof DirectoryNotEmptyException) { LOG.error("Unlock failed. Mount point not an empty directory: {}", task.getException().getMessage()); window.setScene(invalidMountPointScene.get()); } else { diff --git a/main/ui/src/main/resources/fxml/vault_detail.fxml b/main/ui/src/main/resources/fxml/vault_detail.fxml index 349db1f4c..e171e0887 100644 --- a/main/ui/src/main/resources/fxml/vault_detail.fxml +++ b/main/ui/src/main/resources/fxml/vault_detail.fxml @@ -52,5 +52,6 @@ + diff --git a/main/ui/src/main/resources/fxml/vault_detail_unknownerror.fxml b/main/ui/src/main/resources/fxml/vault_detail_unknownerror.fxml new file mode 100644 index 000000000..4c33a67f0 --- /dev/null +++ b/main/ui/src/main/resources/fxml/vault_detail_unknownerror.fxml @@ -0,0 +1,15 @@ + + + + + +