1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

vault timeout fixes

This commit is contained in:
Matt Portune
2022-01-18 22:55:22 -05:00
parent 59e7969856
commit 796cf6dc25
15 changed files with 61 additions and 49 deletions

View File

@@ -270,11 +270,18 @@ namespace Bit.App
private async Task SwitchedAccountAsync()
{
await AppHelpers.OnAccountSwitchAsync();
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
Device.BeginInvokeOnMainThread(async () =>
{
await SetMainPageAsync();
if (shouldTimeout)
{
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
}
else
{
await SetMainPageAsync();
}
UpdateTheme();
await _vaultTimeoutService.CheckVaultTimeoutAsync();
});
}

View File

@@ -7,8 +7,7 @@
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="controls:AccountViewCellViewModel">
<Grid RowSpacing="0"
ColumnSpacing="0"
BackgroundColor="{DynamicResource BackgroundColor}">
ColumnSpacing="0">
<Grid.Resources>
<u:InverseBoolConverter x:Key="inverseBool" />

View File

@@ -89,6 +89,7 @@
<ListView
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>

View File

@@ -183,6 +183,7 @@
<ListView
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>

View File

@@ -132,6 +132,7 @@
<ListView
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>

View File

@@ -182,6 +182,7 @@
<ListView
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>

View File

@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -137,11 +138,15 @@ namespace Bit.App.Pages
{
get
{
// create a separate collection that includes the "add new" row
var accounts = new ExtendedObservableCollection<AccountView>();
accounts.AddRange(_stateService.AccountViews);
accounts.Add(new AccountView());
return accounts;
if (_stateService.AccountViews.Count < Constants.MaxAccounts)
{
// create a separate collection that includes the "add new" row
var accountViews = new ExtendedObservableCollection<AccountView>();
accountViews.AddRange(_stateService.AccountViews);
accountViews.Add(new AccountView());
return accountViews;
}
return _stateService.AccountViews;
}
}

View File

@@ -25,7 +25,7 @@ namespace Bit.Core.Abstractions
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
Task<SymmetricCryptoKey> GetKeyAsync();
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
Task<string> GetKeyHashAsync();
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
@@ -34,7 +34,7 @@ namespace Bit.Core.Abstractions
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key);
Task<bool> HasEncKeyAsync();
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
Task<bool> HasKeyAsync();
Task<bool> HasKeyAsync(string userId = null);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations);
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations,

View File

@@ -6,10 +6,12 @@ namespace Bit.Core.Abstractions
public interface IVaultTimeoutService
{
Task CheckVaultTimeoutAsync();
Task<bool> ShouldTimeoutAsync(string userId = null);
Task ExecuteTimeoutActionAsync(string userId = null);
Task ClearAsync(string userId = null);
Task<bool> IsLockedAsync(string userId = null);
Task<Tuple<bool, bool>> IsPinLockSetAsync();
Task<bool> IsBiometricLockSetAsync();
Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
Task<bool> IsBiometricLockSetAsync(string userId = null);
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
Task LogOutAsync(bool userInitiated = true, string userId = null);
Task SetVaultTimeoutOptionsAsync(int? timeout, string action);

View File

@@ -2,6 +2,7 @@
{
public static class Constants
{
public const int MaxAccounts = 5;
public const string AndroidAppProtocol = "androidapp://";
public const string iOSAppProtocol = "iosapp://";
public static string StateKey = "state";

View File

@@ -5,7 +5,6 @@ namespace Bit.Core.Models.Domain
{
public class Account : Domain
{
public AuthenticationStatus? AuthStatus;
public AccountProfile Profile;
public AccountTokens Tokens;
public AccountSettings Settings;
@@ -24,7 +23,6 @@ namespace Bit.Core.Models.Domain
public Account(Account account)
{
// Copy constructor excludes Keys (for storage)
AuthStatus = account.AuthStatus;
Profile = new AccountProfile(account.Profile);
Tokens = new AccountTokens(account.Tokens);
Settings = new AccountSettings(account.Settings);

View File

@@ -15,7 +15,6 @@ namespace Bit.Core.Models.View
return;
}
IsAccount = true;
AuthStatus = a.AuthStatus;
UserId = a.Profile?.UserId;
Email = a.Profile?.Email;
Hostname = a.Settings?.EnvironmentUrls?.Base;

View File

@@ -80,18 +80,18 @@ namespace Bit.Core.Services
await _stateService.SetOrgKeysEncryptedAsync(orgKeys);
}
public async Task<SymmetricCryptoKey> GetKeyAsync()
public async Task<SymmetricCryptoKey> GetKeyAsync(string userId = null)
{
var inMemoryKey = await _stateService.GetKeyDecryptedAsync();
var inMemoryKey = await _stateService.GetKeyDecryptedAsync(userId);
if (inMemoryKey != null)
{
return inMemoryKey;
}
var key = await _stateService.GetKeyEncryptedAsync();
var key = await _stateService.GetKeyEncryptedAsync(userId);
if (key != null)
{
inMemoryKey = new SymmetricCryptoKey(Convert.FromBase64String(key));
await _stateService.SetKeyDecryptedAsync(inMemoryKey);
await _stateService.SetKeyDecryptedAsync(inMemoryKey, userId);
}
return inMemoryKey;
}
@@ -295,9 +295,9 @@ namespace Bit.Core.Services
return false;
}
public async Task<bool> HasKeyAsync()
public async Task<bool> HasKeyAsync(string userId = null)
{
var key = await GetKeyAsync();
var key = await GetKeyAsync(userId);
return key != null;
}

View File

@@ -89,12 +89,17 @@ namespace Bit.Core.Services
{
await CheckStateAsync();
AccountViews = new ExtendedObservableCollection<AccountView>();
if (AccountViews == null)
{
AccountViews = new ExtendedObservableCollection<AccountView>();
}
AccountViews.Clear();
var accountList = _state?.Accounts?.Values.ToList();
if (accountList == null)
{
return;
}
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
foreach (var account in accountList)
{
var accountView = new AccountView(account);
@@ -104,7 +109,9 @@ namespace Bit.Core.Services
}
else
{
if (await IsLockedAsync(accountView.UserId))
var isLocked = await vaultTimeoutService.IsLockedAsync(accountView.UserId);
var shouldTimeout = await vaultTimeoutService.ShouldTimeoutAsync(accountView.UserId);
if (isLocked || shouldTimeout)
{
var action = account.Settings.VaultTimeoutAction;
accountView.AuthStatus = action == "logOut" ? AuthenticationStatus.LoggedOut

View File

@@ -54,10 +54,10 @@ namespace Bit.Core.Services
public async Task<bool> IsLockedAsync(string userId = null)
{
var hasKey = await _cryptoService.HasKeyAsync();
var hasKey = await _cryptoService.HasKeyAsync(userId);
if (hasKey)
{
var biometricSet = await IsBiometricLockSetAsync();
var biometricSet = await IsBiometricLockSetAsync(userId);
if (biometricSet && _stateService.BiometricLocked)
{
return true;
@@ -73,16 +73,13 @@ namespace Bit.Core.Services
return;
}
foreach (var userId in await _stateService.GetUserIdsAsync())
if (await ShouldTimeoutAsync())
{
if (userId != null && await ShouldLockAsync(userId))
{
await ExecuteTimeoutActionAsync(userId);
}
await ExecuteTimeoutActionAsync();
}
}
private async Task<bool> ShouldLockAsync(string userId)
public async Task<bool> ShouldTimeoutAsync(string userId = null)
{
var authed = await _stateService.IsAuthenticatedAsync(userId);
if (!authed)
@@ -108,7 +105,7 @@ namespace Bit.Core.Services
return diffMs >= vaultTimeoutMs;
}
private async Task ExecuteTimeoutActionAsync(string userId)
public async Task ExecuteTimeoutActionAsync(string userId = null)
{
var action = await _stateService.GetVaultTimeoutActionAsync(userId);
if (action == "logOut")
@@ -130,8 +127,8 @@ namespace Bit.Core.Services
}
if (await _keyConnectorService.GetUsesKeyConnector()) {
var pinSet = await IsPinLockSetAsync();
var pinLock = (pinSet.Item1 && _stateService.GetPinProtectedAsync() != null) || pinSet.Item2;
var pinSet = await IsPinLockSetAsync(userId);
var pinLock = (pinSet.Item1 && _stateService.GetPinProtectedAsync(userId) != null) || pinSet.Item2;
if (!pinLock && !await IsBiometricLockSetAsync())
{
@@ -142,21 +139,14 @@ namespace Bit.Core.Services
if (allowSoftLock)
{
var biometricLocked = await IsBiometricLockSetAsync();
_stateService.BiometricLocked = biometricLocked;
if (biometricLocked)
_stateService.BiometricLocked = await IsBiometricLockSetAsync();
if (_stateService.BiometricLocked)
{
_messagingService.Send("locked", userInitiated);
_lockedCallback?.Invoke(userInitiated);
return;
}
}
if (userId == null || userId == await _stateService.GetActiveUserIdAsync())
{
_searchService.ClearIndex();
}
await Task.WhenAll(
_cryptoService.ClearKeyAsync(userId),
_cryptoService.ClearOrgKeysAsync(true, userId),
@@ -187,16 +177,16 @@ namespace Bit.Core.Services
await _tokenService.ToggleTokensAsync();
}
public async Task<Tuple<bool, bool>> IsPinLockSetAsync()
public async Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null)
{
var protectedPin = await _stateService.GetProtectedPinAsync();
var pinProtectedKey = await _stateService.GetPinProtectedAsync();
var protectedPin = await _stateService.GetProtectedPinAsync(userId);
var pinProtectedKey = await _stateService.GetPinProtectedAsync(userId);
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
}
public async Task<bool> IsBiometricLockSetAsync()
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
{
var biometricLock = await _stateService.GetBiometricUnlockAsync();
var biometricLock = await _stateService.GetBiometricUnlockAsync(userId);
return biometricLock.GetValueOrDefault();
}