1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-10 05:13:31 +00:00
Files
mobile/src/Core/Services/VaultTimeoutService.cs
2023-08-01 08:46:02 -04:00

269 lines
9.9 KiB
C#

using System;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
namespace Bit.Core.Services
{
public enum PinLockType
{
Disabled,
Persistent,
Transient
}
public class VaultTimeoutService : IVaultTimeoutService
{
private readonly ICryptoService _cryptoService;
private readonly IStateService _stateService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IFolderService _folderService;
private readonly ICipherService _cipherService;
private readonly ICollectionService _collectionService;
private readonly ISearchService _searchService;
private readonly IMessagingService _messagingService;
private readonly ITokenService _tokenService;
private readonly IKeyConnectorService _keyConnectorService;
private readonly Func<Tuple<string, bool>, Task> _lockedCallback;
private readonly Func<Tuple<string, bool, bool>, Task> _loggedOutCallback;
public VaultTimeoutService(
ICryptoService cryptoService,
IStateService stateService,
IPlatformUtilsService platformUtilsService,
IFolderService folderService,
ICipherService cipherService,
ICollectionService collectionService,
ISearchService searchService,
IMessagingService messagingService,
ITokenService tokenService,
IKeyConnectorService keyConnectorService,
Func<Tuple<string, bool>, Task> lockedCallback,
Func<Tuple<string, bool, bool>, Task> loggedOutCallback)
{
_cryptoService = cryptoService;
_stateService = stateService;
_platformUtilsService = platformUtilsService;
_folderService = folderService;
_cipherService = cipherService;
_collectionService = collectionService;
_searchService = searchService;
_messagingService = messagingService;
_tokenService = tokenService;
_keyConnectorService = keyConnectorService;
_lockedCallback = lockedCallback;
_loggedOutCallback = loggedOutCallback;
}
public long? DelayLockAndLogoutMs { get; set; }
public async Task<bool> IsLockedAsync(string userId = null)
{
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
if (hasKey)
{
var biometricSet = await IsBiometricLockSetAsync(userId);
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
{
return true;
}
}
return !hasKey;
}
public async Task<bool> ShouldLockAsync(string userId = null)
{
return await ShouldTimeoutAsync(userId)
&&
await _stateService.GetVaultTimeoutActionAsync(userId) == VaultTimeoutAction.Lock;
}
public async Task<bool> IsLoggedOutByTimeoutAsync(string userId = null)
{
var authed = await _stateService.IsAuthenticatedAsync(userId);
var email = await _stateService.GetEmailAsync(userId);
return !authed && !string.IsNullOrWhiteSpace(email);
}
public async Task<bool> ShouldLogOutByTimeoutAsync(string userId = null)
{
return await ShouldTimeoutAsync(userId)
&&
await _stateService.GetVaultTimeoutActionAsync(userId) == VaultTimeoutAction.Logout;
}
public async Task CheckVaultTimeoutAsync()
{
if (_platformUtilsService.IsViewOpen())
{
return;
}
if (await ShouldTimeoutAsync())
{
await ExecuteTimeoutActionAsync();
}
}
public async Task<bool> ShouldTimeoutAsync(string userId = null)
{
var authed = await _stateService.IsAuthenticatedAsync(userId);
if (!authed)
{
return false;
}
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync(userId);
if (vaultTimeoutAction == VaultTimeoutAction.Lock && await IsLockedAsync(userId))
{
return false;
}
var vaultTimeoutMinutes = await GetVaultTimeout(userId);
if (vaultTimeoutMinutes < 0 || vaultTimeoutMinutes == null)
{
return false;
}
if (vaultTimeoutMinutes == 0 && !DelayLockAndLogoutMs.HasValue)
{
return true;
}
var lastActiveTime = await _stateService.GetLastActiveTimeAsync(userId);
if (lastActiveTime == null)
{
return false;
}
var diffMs = _platformUtilsService.GetActiveTime() - lastActiveTime;
if (DelayLockAndLogoutMs.HasValue && diffMs < DelayLockAndLogoutMs)
{
return false;
}
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
return diffMs >= vaultTimeoutMs;
}
public async Task ExecuteTimeoutActionAsync(string userId = null)
{
var action = await _stateService.GetVaultTimeoutActionAsync(userId);
if (action == VaultTimeoutAction.Logout)
{
await LogOutAsync(false, userId);
}
else
{
await LockAsync(true, false, userId);
}
}
public async Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null)
{
var authed = await _stateService.IsAuthenticatedAsync(userId);
if (!authed)
{
return;
}
var isActiveAccount = await _stateService.IsActiveAccountAsync(userId);
if (userId == null)
{
userId = await _stateService.GetActiveUserIdAsync();
}
if (await _keyConnectorService.GetUsesKeyConnector())
{
var pinStatus = await GetPinLockTypeAsync(userId);
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync();
var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
pinStatus == PinLockType.Persistent;
if (!pinEnabled && !await IsBiometricLockSetAsync())
{
await LogOutAsync(userInitiated, userId);
return;
}
}
if (allowSoftLock)
{
var isBiometricLockSet = await IsBiometricLockSetAsync(userId);
await _stateService.SetBiometricLockedAsync(isBiometricLockSet, userId);
if (isBiometricLockSet)
{
_lockedCallback?.Invoke(new Tuple<string, bool>(userId, userInitiated));
return;
}
}
await Task.WhenAll(
_cryptoService.ClearUserKeyAsync(userId),
_cryptoService.ClearMasterKeyAsync(userId),
_cryptoService.ClearOrgKeysAsync(true, userId),
_cryptoService.ClearKeyPairAsync(true, userId));
if (isActiveAccount)
{
_folderService.ClearCache();
await _cipherService.ClearCacheAsync();
_collectionService.ClearCache();
_searchService.ClearIndex();
}
_lockedCallback?.Invoke(new Tuple<string, bool>(userId, userInitiated));
}
public async Task LogOutAsync(bool userInitiated = true, string userId = null)
{
if (_loggedOutCallback != null)
{
await _loggedOutCallback.Invoke(new Tuple<string, bool, bool>(userId, userInitiated, false));
}
}
public async Task SetVaultTimeoutOptionsAsync(int? timeout, VaultTimeoutAction? action)
{
await _stateService.SetVaultTimeoutAsync(timeout);
await _stateService.SetVaultTimeoutActionAsync(action);
await _cryptoService.RefreshKeysAsync();
await _tokenService.ToggleTokensAsync();
}
public async Task<PinLockType> GetPinLockTypeAsync(string userId = null)
{
// we can't depend on only the protected pin being set because old
// versions only used it for MP on Restart
var isPinEnabled = await _stateService.GetProtectedPinAsync(userId) != null;
var hasUserKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null;
var hasOldUserKeyPin = await _stateService.GetPinProtectedAsync(userId) != null;
if (hasUserKeyPin || hasOldUserKeyPin)
{
return PinLockType.Persistent;
}
else if (isPinEnabled && !hasUserKeyPin && !hasOldUserKeyPin)
{
return PinLockType.Transient;
}
return PinLockType.Disabled;
}
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
{
var biometricLock = await _stateService.GetBiometricUnlockAsync(userId);
return biometricLock.GetValueOrDefault();
}
public async Task ClearAsync(string userId = null)
{
await _cryptoService.ClearPinKeysAsync(userId);
}
public async Task<int?> GetVaultTimeout(string userId = null)
{
return await _stateService.GetVaultTimeoutAsync(userId);
}
public async Task<VaultTimeoutAction?> GetVaultTimeoutAction(string userId = null)
{
return await _stateService.GetVaultTimeoutActionAsync(userId);
}
}
}