converted lock after "time" to "idle time"

This commit is contained in:
Sebastian Stenzel
2021-06-02 15:53:57 +02:00
parent cd7acea54e
commit 8f8fa0248c
7 changed files with 109 additions and 31 deletions

View File

@@ -15,6 +15,7 @@ import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultListModule;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.frontend.webdav.WebDavServer;
import org.slf4j.Logger;
@@ -37,7 +38,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Module(subcomponents = {VaultComponent.class}, includes = {KeychainModule.class})
@Module(subcomponents = {VaultComponent.class}, includes = {VaultListModule.class, KeychainModule.class})
public abstract class CommonsModule {
private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class);
@@ -87,12 +88,6 @@ public abstract class CommonsModule {
return settingsProvider.get();
}
@Provides
@Singleton
static ObservableList<Vault> provideVaultList(VaultListManager vaultListManager) {
return vaultListManager.getVaultList();
}
@Provides
@Singleton
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {

View File

@@ -0,0 +1,62 @@
package org.cryptomator.common.vaults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.ObservableList;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Singleton
public class AutoLocker {
private static final Logger LOG = LoggerFactory.getLogger(AutoLocker.class);
private final ScheduledExecutorService scheduler;
private final ObservableList<Vault> vaultList;
@Inject
public AutoLocker(ScheduledExecutorService scheduler, ObservableList<Vault> vaultList) {
this.scheduler = scheduler;
this.vaultList = vaultList;
}
public void init() {
scheduler.scheduleAtFixedRate(this::tick, 0, 1, TimeUnit.MINUTES);
}
private void tick() {
vaultList.stream() // all vaults
.filter(Vault::isUnlocked) // unlocked vaults
.filter(this::exceedsIdleTime) // idle vaults
.forEach(this::autolock);
}
private void autolock(Vault vault) {
try {
vault.lock(false);
LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
} catch (Volume.VolumeException | LockNotCompletedException e) {
LOG.error("Autolocking failed.", e);
}
}
private boolean exceedsIdleTime(Vault vault) {
assert vault.isUnlocked();
// TODO: shouldn't we read these properties from within FX Application Thread?
if (vault.getVaultSettings().lockAfterTime().get()) {
int maxIdleMinutes = vault.getVaultSettings().lockTimeInMinutes().get();
var idleSince = vault.getStats().getLastActivity();
var threshold = idleSince.plus(maxIdleMinutes, ChronoUnit.MINUTES);
return threshold.isBefore(Instant.now());
} else {
return false;
}
}
}

View File

@@ -37,22 +37,21 @@ public class VaultListManager {
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
private final AutoLocker autoLocker;
private final VaultComponent.Builder vaultComponentBuilder;
private final ObservableList<Vault> vaultList;
private final String defaultVaultName;
@Inject
public VaultListManager(VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
this.vaultList = vaultList;
this.autoLocker = autoLocker;
this.vaultComponentBuilder = vaultComponentBuilder;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
this.vaultList = FXCollections.observableArrayList(Vault::observables);
addAll(settings.getDirectories());
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
}
public ObservableList<Vault> getVaultList() {
return vaultList;
autoLocker.init();
}
public Vault add(Path pathToVault) throws IOException {

View File

@@ -0,0 +1,19 @@
package org.cryptomator.common.vaults;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@Module
public class VaultListModule {
@Provides
@Singleton
public ObservableList<Vault> provideVaultList() {
return FXCollections.observableArrayList(Vault::observables);
}
}

View File

@@ -13,9 +13,11 @@ import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
@@ -39,6 +41,7 @@ public class VaultStats {
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
private final LongProperty filesRead = new SimpleLongProperty();
private final LongProperty filesWritten = new SimpleLongProperty();
private final ObjectProperty<Instant> lastActivity = new SimpleObjectProperty<>();
@Inject
VaultStats(AtomicReference<CryptoFileSystem> fs, VaultState state, ExecutorService executor) {
@@ -73,9 +76,16 @@ public class VaultStats {
toalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
totalBytesEncrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesEncrypted).orElse(0L));
totalBytesDecrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesDecrypted).orElse(0L));
var oldAccessCount = filesRead.get() + filesWritten.get();
filesRead.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesRead).orElse(0L));
filesWritten.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesWritten).orElse(0L));
var newAccessCount = filesRead.get() + filesWritten.get();
// check for any I/O activity
if (newAccessCount > oldAccessCount) {
LOG.info("ACTIVITY!");
lastActivity.set(Instant.now());
}
}
private double getCacheHitRate(CryptoFileSystemStats stats) {
@@ -175,4 +185,12 @@ public class VaultStats {
public LongProperty filesWritten() {return filesWritten;}
public long getFilesWritten() {return filesWritten.get();}
public ObjectProperty<Instant> lastActivityProperty() {
return lastActivity;
}
public Instant getLastActivity() {
return lastActivity.get();
}
}

View File

@@ -59,11 +59,9 @@ public class FxApplication extends Application {
private final ObservableList<Window> visibleWindows;
private final BooleanBinding hasVisibleWindows;
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
private final VaultListManager vaultListManager;
private final ScheduledExecutorService scheduledExecutorService;
@Inject
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder, VaultListManager vaultListManager, ScheduledExecutorService scheduledExecutorService) {
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
this.settings = settings;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
@@ -77,8 +75,6 @@ public class FxApplication extends Application {
this.licenseHolder = licenseHolder;
this.visibleWindows = Stage.getWindows().filtered(Window::isShowing);
this.hasVisibleWindows = Bindings.isNotEmpty(visibleWindows);
this.vaultListManager = vaultListManager;
this.scheduledExecutorService = scheduledExecutorService;
}
public void start() {
@@ -131,7 +127,6 @@ public class FxApplication extends Application {
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
}
});
checkAutolock(vault, owner);
}
public void startLockWorkflow(Vault vault, Optional<Stage> owner) {
@@ -212,14 +207,4 @@ public class FxApplication extends Application {
});
}
private void checkAutolock(Vault vault, Optional<Stage> owner) {
if (vault.getVaultSettings().lockAfterTime().get()) {
LOG.info("Locking after {} minutes.", vault.getVaultSettings().lockTimeInMinutes().get());
scheduledExecutorService.schedule(() -> {
startLockWorkflow(vault, owner);
}, (long) (vault.getVaultSettings().lockTimeInMinutes().get()), TimeUnit.MINUTES);
}
}
}

View File

@@ -331,8 +331,8 @@ vaultOptions.masterkey.showRecoveryKeyBtn=Display Recovery Key
vaultOptions.masterkey.recoverPasswordBtn=Recover Password
## Auto Lock
vaultOptions.autoLock=Auto-Lock
vaultOptions.autoLock.lockAfterTimePart1=Lock after
vaultOptions.autoLock.lockAfterTimePart2=minutes.
vaultOptions.autoLock.lockAfterTimePart1=Lock when idle for
vaultOptions.autoLock.lockAfterTimePart2=minutes
# Recovery Key
recoveryKey.title=Recovery Key