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

Compare commits

...

19 Commits

Author SHA1 Message Date
Federico Maccaroni
a4e599f680 WIP Fix toolbar item avatar update issue iOS 2022-01-21 11:08:12 -03:00
Matt Portune
d41e1a0bc3 logout fixes and token handling improvements 2022-01-19 16:15:09 -05:00
Matt Portune
e717992b2b account list status enhancements 2022-01-19 12:06:45 -05:00
Matt Portune
d52a1bf668 Merge branch 'master' into feature-accountswitch 2022-01-19 09:31:13 -05:00
Matt Portune
796cf6dc25 vault timeout fixes 2022-01-18 22:55:22 -05:00
Matt Portune
59e7969856 cleanup 2022-01-18 19:31:32 -05:00
Matt Portune
afecc1f569 fixes 2022-01-18 17:43:20 -05:00
Matt Portune
a3922f7fbb fixes 2022-01-18 17:21:24 -05:00
Matt Portune
7e288c62f9 switch fixes 2022-01-18 15:20:16 -05:00
Matt Portune
ed2bb9b94e fixes for theme handling on account switching and re-adding existing account 2022-01-18 14:50:04 -05:00
Matt Portune
1c6a8964fe fixed Send tests 2022-01-18 13:51:09 -05:00
Matt Portune
6aa96b8964 Merge branch 'master' into feature-accountswitch 2022-01-18 10:36:54 -05:00
Matt Portune
f6d4b0a443 updates to send test logic 2022-01-18 10:28:34 -05:00
Matt Portune
e20ae26808 wip 2022-01-17 23:15:32 -05:00
Matt Portune
e3a8d3bb55 Merge branch 'master' into feature-accountswitch 2022-01-17 21:59:39 -05:00
Matt Portune
65739489a7 wip 2022-01-17 21:58:04 -05:00
Matt Portune
120fb70039 WIP 2022-01-17 00:41:14 -05:00
Matt Portune
d1ece79d2e Merge branch 'master' into feature-accountswitch 2022-01-16 22:48:04 -05:00
Matt Portune
ebae2585f6 Account switching 2022-01-16 22:43:37 -05:00
130 changed files with 4027 additions and 1596 deletions

View File

@@ -10,7 +10,6 @@ using Android.Views;
using Android.Views.Accessibility; using Android.Views.Accessibility;
using Android.Widget; using Android.Widget;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -25,7 +24,7 @@ namespace Bit.Droid.Accessibility
private const string BitwardenPackage = "com.x8bit.bitwarden"; private const string BitwardenPackage = "com.x8bit.bitwarden";
private const string BitwardenWebsite = "vault.bitwarden.com"; private const string BitwardenWebsite = "vault.bitwarden.com";
private IStorageService _storageService; private IStateService _stateService;
private IBroadcasterService _broadcasterService; private IBroadcasterService _broadcasterService;
private DateTime? _lastSettingsReload = null; private DateTime? _lastSettingsReload = null;
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1); private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
@@ -444,9 +443,9 @@ namespace Bit.Droid.Accessibility
private void LoadServices() private void LoadServices()
{ {
if (_storageService == null) if (_stateService == null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
} }
if (_broadcasterService == null) if (_broadcasterService == null)
{ {
@@ -460,12 +459,12 @@ namespace Bit.Droid.Accessibility
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan) if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
{ {
_lastSettingsReload = now; _lastSettingsReload = now;
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey); var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
if (uris != null) if (uris != null)
{ {
_blacklistedUris = new HashSet<string>(uris); _blacklistedUris = new HashSet<string>(uris);
} }
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded); var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault(); AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
} }
} }

View File

@@ -171,7 +171,8 @@
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" /> <AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" /> <AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
<AndroidResource Include="Resources\drawable\card.xml" /> <AndroidResource Include="Resources\drawable\card.xml" />
<AndroidResource Include="Resources\drawable\cog.xml" /> <AndroidResource Include="Resources\drawable\cog_environment.xml" />
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
<AndroidResource Include="Resources\drawable\icon.xml" /> <AndroidResource Include="Resources\drawable\icon.xml" />
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" /> <AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
<AndroidResource Include="Resources\drawable\ic_warning.xml" /> <AndroidResource Include="Resources\drawable\ic_warning.xml" />

View File

@@ -22,9 +22,8 @@ namespace Bit.Droid.Autofill
{ {
private ICipherService _cipherService; private ICipherService _cipherService;
private IVaultTimeoutService _vaultTimeoutService; private IVaultTimeoutService _vaultTimeoutService;
private IStorageService _storageService;
private IPolicyService _policyService; private IPolicyService _policyService;
private IUserService _userService; private IStateService _stateService;
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) FillCallback callback)
@@ -38,18 +37,18 @@ namespace Bit.Droid.Autofill
var parser = new Parser(structure, ApplicationContext); var parser = new Parser(structure, ApplicationContext);
parser.Parse(); parser.Parse();
if (_storageService == null) if (_stateService == null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
} }
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService); var shouldAutofill = await parser.ShouldAutofillAsync(_stateService);
if (!shouldAutofill) if (!shouldAutofill)
{ {
return; return;
} }
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true; var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
if (_vaultTimeoutService == null) if (_vaultTimeoutService == null)
{ {
@@ -81,12 +80,12 @@ namespace Bit.Droid.Autofill
return; return;
} }
if (_storageService == null) if (_stateService == null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
} }
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey); var disableSavePrompt = await _stateService.GetAutofillDisableSavePromptAsync();
if (disableSavePrompt.GetValueOrDefault()) if (disableSavePrompt.GetValueOrDefault())
{ {
return; return;

View File

@@ -80,13 +80,13 @@ namespace Bit.Droid.Autofill
} }
} }
public async Task<bool> ShouldAutofillAsync(IStorageService storageService) public async Task<bool> ShouldAutofillAsync(IStateService stateService)
{ {
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) && var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
FieldCollection != null && FieldCollection.Fillable; FieldCollection != null && FieldCollection.Fillable;
if (fillable) if (fillable)
{ {
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey); var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
if (blacklistedUris != null && blacklistedUris.Count > 0) if (blacklistedUris != null && blacklistedUris.Count > 0)
{ {
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri); fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);

View File

@@ -33,7 +33,7 @@ namespace Bit.Droid
private IDeviceActionService _deviceActionService; private IDeviceActionService _deviceActionService;
private IMessagingService _messagingService; private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService; private IBroadcasterService _broadcasterService;
private IUserService _userService; private IStateService _stateService;
private IAppIdService _appIdService; private IAppIdService _appIdService;
private IEventService _eventService; private IEventService _eventService;
private PendingIntent _eventUploadPendingIntent; private PendingIntent _eventUploadPendingIntent;
@@ -54,7 +54,7 @@ namespace Bit.Droid
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService"); _appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService"); _eventService = ServiceContainer.Resolve<IEventService>("eventService");
@@ -71,7 +71,7 @@ namespace Bit.Droid
} }
#if !FDROID #if !FDROID
var appCenterHelper = new AppCenterHelper(_appIdService, _userService); var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
var appCenterTask = appCenterHelper.InitAsync(); var appCenterTask = appCenterHelper.InitAsync();
#endif #endif
@@ -373,7 +373,7 @@ namespace Bit.Droid
{ {
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor); Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor); Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled()); ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
} }
private void ExitApp() private void ExitApp()

View File

@@ -97,13 +97,14 @@ namespace Bit.Droid
var secureStorageService = new SecureStorageService(); var secureStorageService = new SecureStorageService();
var cryptoPrimitiveService = new CryptoPrimitiveService(); var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage); var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService, var stateService = new StateService(mobileStorageService, secureStorageService);
var deviceActionService = new DeviceActionService(stateService, messagingService,
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService")); broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService, var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService); broadcasterService);
var biometricService = new BiometricService(); var biometricService = new BiometricService();
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService); var cryptoService = new CryptoService(stateService, cryptoFunctionService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService); ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
@@ -113,7 +114,8 @@ namespace Bit.Droid
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService); ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService); ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService); ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(mobileStorageService)); ServiceContainer.Register<IStateService>("stateService", stateService);
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService); ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService); ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
ServiceContainer.Register<IBiometricService>("biometricService", biometricService); ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
@@ -132,7 +134,7 @@ namespace Bit.Droid
ServiceContainer.Register<IPushNotificationListenerService>( ServiceContainer.Register<IPushNotificationListenerService>(
"pushNotificationListenerService", notificationListenerService); "pushNotificationListenerService", notificationListenerService);
var androidPushNotificationService = new AndroidPushNotificationService( var androidPushNotificationService = new AndroidPushNotificationService(
mobileStorageService, notificationListenerService); stateService, notificationListenerService);
ServiceContainer.Register<IPushNotificationService>( ServiceContainer.Register<IPushNotificationService>(
"pushNotificationService", androidPushNotificationService); "pushNotificationService", androidPushNotificationService);
#endif #endif
@@ -148,10 +150,6 @@ namespace Bit.Droid
private async Task BootstrapAsync() private async Task BootstrapAsync()
{ {
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService")
.GetAsync<bool?>(Constants.DisableFaviconKey);
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
Constants.DisableFaviconKey, disableFavicon);
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync(); await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
} }
} }

View File

@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
{ {
public async override void OnNewToken(string token) public async override void OnNewToken(string token)
{ {
var storageService = ServiceContainer.Resolve<IStorageService>("storageService"); var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService"); var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token); await stateService.SetPushRegisteredTokenAsync(token);
await pushNotificationService.RegisterAsync(); await pushNotificationService.RegisterAsync();
} }

View File

@@ -1,5 +1,4 @@
using System; using Android.App;
using Android.App;
using Android.Content; using Android.Content;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Utilities; using Bit.App.Utilities;
@@ -14,9 +13,10 @@ namespace Bit.Droid.Receivers
{ {
public override async void OnReceive(Context context, Intent intent) public override async void OnReceive(Context context, Intent intent)
{ {
var storageService = ServiceContainer.Resolve<IStorageService>("storageService"); await AppHelpers.PerformUpdateTasksAsync(
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"), ServiceContainer.Resolve<ISyncService>("syncService"),
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService); ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
ServiceContainer.Resolve<IStateService>("stateService"));
} }
} }
} }

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M1152,896q0,-106 -75,-181t-181,-75 -181,75 -75,181 75,181 181,75 181,-75 75,-181zM1664,787v222q0,12 -8,23t-20,13l-185,28q-19,54 -39,91 35,50 107,138 10,12 10,25t-9,23q-27,37 -99,108t-94,71q-12,0 -26,-9l-138,-108q-44,23 -91,38 -16,136 -29,186 -7,28 -36,28h-222q-14,0 -24.5,-8.5t-11.5,-21.5l-28,-184q-49,-16 -90,-37l-141,107q-10,9 -25,9 -14,0 -25,-11 -126,-114 -165,-168 -7,-10 -7,-23 0,-12 8,-23 15,-21 51,-66.5t54,-70.5q-27,-50 -41,-99l-183,-27q-13,-2 -21,-12.5t-8,-23.5v-222q0,-12 8,-23t19,-13l186,-28q14,-46 39,-92 -40,-57 -107,-138 -10,-12 -10,-24 0,-10 9,-23 26,-36 98.5,-107.5t94.5,-71.5q13,0 26,10l138,107q44,-23 91,-38 16,-136 29,-186 7,-28 36,-28h222q14,0 24.5,8.5t11.5,21.5l28,184q49,16 90,37l142,-107q9,-9 24,-9 13,0 25,10 129,119 165,170 7,8 7,22 0,12 -8,23 -15,21 -51,66.5t-54,70.5q26,50 41,98l183,28q13,2 21,12.5t8,23.5z" />
</vector>

View File

@@ -3,7 +3,6 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using AndroidX.Core.App; using AndroidX.Core.App;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Xamarin.Forms; using Xamarin.Forms;
@@ -11,14 +10,14 @@ namespace Bit.Droid.Services
{ {
public class AndroidPushNotificationService : IPushNotificationService public class AndroidPushNotificationService : IPushNotificationService
{ {
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly IPushNotificationListenerService _pushNotificationListenerService; private readonly IPushNotificationListenerService _pushNotificationListenerService;
public AndroidPushNotificationService( public AndroidPushNotificationService(
IStorageService storageService, IStateService stateService,
IPushNotificationListenerService pushNotificationListenerService) IPushNotificationListenerService pushNotificationListenerService)
{ {
_storageService = storageService; _stateService = stateService;
_pushNotificationListenerService = pushNotificationListenerService; _pushNotificationListenerService = pushNotificationListenerService;
} }
@@ -26,12 +25,12 @@ namespace Bit.Droid.Services
public async Task<string> GetTokenAsync() public async Task<string> GetTokenAsync()
{ {
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey); return await _stateService.GetPushCurrentTokenAsync();
} }
public async Task RegisterAsync() public async Task RegisterAsync()
{ {
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey); var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
var currentToken = await GetTokenAsync(); var currentToken = await GetTokenAsync();
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken) if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
{ {
@@ -39,7 +38,7 @@ namespace Bit.Droid.Services
} }
else else
{ {
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow); await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
} }
} }

View File

@@ -12,12 +12,12 @@ namespace Bit.Droid.Services
{ {
public class ClipboardService : IClipboardService public class ClipboardService : IClipboardService
{ {
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent; private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
public ClipboardService(IStorageService storageService) public ClipboardService(IStateService stateService)
{ {
_storageService = storageService; _stateService = stateService;
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() => _clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity, PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
@@ -39,7 +39,7 @@ namespace Bit.Droid.Services
if (clearMs < 0) if (clearMs < 0)
{ {
// if not set then we need to check if the user set this config // if not set then we need to check if the user set this config
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey); var clearSeconds = await _stateService.GetClearClipboardAsync();
if (clearSeconds != null) if (clearSeconds != null)
{ {
clearMs = clearSeconds.Value * 1000; clearMs = clearSeconds.Value * 1000;

View File

@@ -33,7 +33,7 @@ namespace Bit.Droid.Services
{ {
public class DeviceActionService : IDeviceActionService public class DeviceActionService : IDeviceActionService
{ {
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IBroadcasterService _broadcasterService; private readonly IBroadcasterService _broadcasterService;
private readonly Func<IEventService> _eventServiceFunc; private readonly Func<IEventService> _eventServiceFunc;
@@ -43,12 +43,12 @@ namespace Bit.Droid.Services
private string _userAgent; private string _userAgent;
public DeviceActionService( public DeviceActionService(
IStorageService storageService, IStateService stateService,
IMessagingService messagingService, IMessagingService messagingService,
IBroadcasterService broadcasterService, IBroadcasterService broadcasterService,
Func<IEventService> eventServiceFunc) Func<IEventService> eventServiceFunc)
{ {
_storageService = storageService; _stateService = stateService;
_messagingService = messagingService; _messagingService = messagingService;
_broadcasterService = broadcasterService; _broadcasterService = broadcasterService;
_eventServiceFunc = eventServiceFunc; _eventServiceFunc = eventServiceFunc;
@@ -250,7 +250,7 @@ namespace Bit.Droid.Services
try try
{ {
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir); DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow); await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
} }
catch (Exception) { } catch (Exception) { }
} }
@@ -833,9 +833,8 @@ namespace Bit.Droid.Services
{ {
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp)) if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
{ {
var userService = ServiceContainer.Resolve<IUserService>("userService"); var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey); var canAccessPremium = await _stateService.CanAccessPremiumAsync();
var canAccessPremium = await userService.CanAccessPremiumAsync();
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault()) if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
{ {
var totpService = ServiceContainer.Resolve<ITotpService>("totpService"); var totpService = ServiceContainer.Resolve<ITotpService>("totpService");

View File

@@ -4,7 +4,6 @@ using Android.Content;
using Android.Runtime; using Android.Runtime;
using Android.Service.QuickSettings; using Android.Service.QuickSettings;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.Droid.Accessibility; using Bit.Droid.Accessibility;
@@ -18,7 +17,7 @@ namespace Bit.Droid.Tile
[Register("com.x8bit.bitwarden.AutofillTileService")] [Register("com.x8bit.bitwarden.AutofillTileService")]
public class AutofillTileService : TileService public class AutofillTileService : TileService
{ {
private IStorageService _storageService; private IStateService _stateService;
public override void OnTileAdded() public override void OnTileAdded()
{ {
@@ -59,11 +58,11 @@ namespace Bit.Droid.Tile
private void SetTileAdded(bool isAdded) private void SetTileAdded(bool isAdded)
{ {
AccessibilityHelpers.IsAutofillTileAdded = isAdded; AccessibilityHelpers.IsAutofillTileAdded = isAdded;
if (_storageService == null) if (_stateService == null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
} }
_storageService.SaveAsync(Constants.AutofillTileAdded, isAdded); _stateService.SetAutofillTileAddedAsync(isAdded);
} }
private void ScanAndFill() private void ScanAndFill()

View File

@@ -12,22 +12,22 @@ namespace Bit.Droid.Utilities
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42"; private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
private readonly IAppIdService _appIdService; private readonly IAppIdService _appIdService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private string _userId; private string _userId;
private string _appId; private string _appId;
public AppCenterHelper( public AppCenterHelper(
IAppIdService appIdService, IAppIdService appIdService,
IUserService userService) IStateService stateService)
{ {
_appIdService = appIdService; _appIdService = appIdService;
_userService = userService; _stateService = stateService;
} }
public async Task InitAsync() public async Task InitAsync()
{ {
_userId = await _userService.GetUserIdAsync(); _userId = await _stateService.GetActiveUserIdAsync();
_appId = await _appIdService.GetAppIdAsync(); _appId = await _appIdService.GetAppIdAsync();
AppCenter.Start(AppSecret, typeof(Crashes)); AppCenter.Start(AppSecret, typeof(Crashes));

View File

@@ -15,9 +15,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" /> <PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" /> <PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="1.3.2" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" /> <PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" /> <PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2125" /> <PackageReference Include="Xamarin.Forms" Version="5.0.0.2291" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
</ItemGroup> </ItemGroup>
@@ -120,6 +122,7 @@
<DependentUpon>SendGroupingsPage.xaml</DependentUpon> <DependentUpon>SendGroupingsPage.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -131,6 +134,7 @@
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml"> <EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -4,8 +4,8 @@ using Bit.App.Pages;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Services; using Bit.App.Services;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -17,7 +17,6 @@ namespace Bit.App
{ {
public partial class App : Application public partial class App : Application
{ {
private readonly IUserService _userService;
private readonly IBroadcasterService _broadcasterService; private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
@@ -25,7 +24,6 @@ namespace Bit.App
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
private readonly IStorageService _storageService;
private readonly IStorageService _secureStorageService; private readonly IStorageService _secureStorageService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
@@ -39,7 +37,6 @@ namespace Bit.App
Current = this; Current = this;
return; return;
} }
_userService = ServiceContainer.Resolve<IUserService>("userService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
@@ -47,7 +44,6 @@ namespace Bit.App
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_authService = ServiceContainer.Resolve<IAuthService>("authService"); _authService = ServiceContainer.Resolve<IAuthService>("authService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService"); _secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
@@ -84,8 +80,12 @@ namespace Bit.App
} }
else if (message.Command == "logout") else if (message.Command == "logout")
{ {
var extras = message.Data as Tuple<string, bool, bool>;
var userId = extras?.Item1;
var userInitiated = extras?.Item2;
var expired = extras?.Item3;
Device.BeginInvokeOnMainThread(async () => Device.BeginInvokeOnMainThread(async () =>
await LogOutAsync((message.Data as bool?).GetValueOrDefault())); await LogOutAsync(userId, userInitiated, expired));
} }
else if (message.Command == "loggedOut") else if (message.Command == "loggedOut")
{ {
@@ -106,6 +106,18 @@ namespace Bit.App
await SleptAsync(); await SleptAsync();
} }
} }
else if (message.Command == "addAccount")
{
await AddAccount();
}
else if (message.Command == "accountAdded")
{
UpdateTheme();
}
else if (message.Command == "switchedAccount")
{
await SwitchedAccountAsync();
}
else if (message.Command == "migrated") else if (message.Command == "migrated")
{ {
await Task.Delay(1000); await Task.Delay(1000);
@@ -167,7 +179,7 @@ namespace Bit.App
if (string.IsNullOrWhiteSpace(Options.Uri)) if (string.IsNullOrWhiteSpace(Options.Uri))
{ {
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService, var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_storageService); _stateService);
if (!updated) if (!updated)
{ {
SyncIfNeeded(); SyncIfNeeded();
@@ -191,7 +203,7 @@ namespace Bit.App
var isLocked = await _vaultTimeoutService.IsLockedAsync(); var isLocked = await _vaultTimeoutService.IsLockedAsync();
if (!isLocked) if (!isLocked)
{ {
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime()); await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
} }
SetTabsPageFromAutofill(isLocked); SetTabsPageFromAutofill(isLocked);
await SleptAsync(); await SleptAsync();
@@ -236,27 +248,65 @@ namespace Bit.App
new System.Globalization.UmAlQuraCalendar(); new System.Globalization.UmAlQuraCalendar();
} }
private async Task LogOutAsync(bool expired) private async Task LogOutAsync(string userId, bool? userInitiated, bool? expired)
{ {
await AppHelpers.LogOutAsync(); await AppHelpers.LogOutAsync(userId, userInitiated.GetValueOrDefault(true));
await SetMainPageAsync();
_authService.LogOut(() => _authService.LogOut(() =>
{ {
Current.MainPage = new HomePage(); if (expired.GetValueOrDefault())
if (expired)
{ {
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired); _platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
} }
}); });
} }
private async Task AddAccount()
{
Device.BeginInvokeOnMainThread(async () =>
{
Current.MainPage = new NavigationPage(new HomePage(Options));
});
}
private async Task SwitchedAccountAsync()
{
await AppHelpers.OnAccountSwitchAsync();
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
Device.BeginInvokeOnMainThread(async () =>
{
if (shouldTimeout)
{
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
}
else
{
await SetMainPageAsync();
}
await Task.Delay(50);
UpdateTheme();
});
}
private async Task SetMainPageAsync() private async Task SetMainPageAsync()
{ {
var authed = await _userService.IsAuthenticatedAsync(); var authed = await _stateService.IsAuthenticatedAsync();
if (authed) if (authed)
{ {
if (await _vaultTimeoutService.IsLockedAsync()) var isLocked = await _vaultTimeoutService.IsLockedAsync();
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync();
if (isLocked || shouldTimeout)
{ {
Current.MainPage = new NavigationPage(new LockPage(Options)); if (vaultTimeoutAction == "logOut")
{
var email = await _stateService.GetEmailAsync();
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
}
else
{
Current.MainPage = new NavigationPage(new LockPage(Options));
}
} }
else if (Options.FromAutofillFramework && Options.SaveType.HasValue) else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
{ {
@@ -277,13 +327,13 @@ namespace Bit.App
} }
else else
{ {
Current.MainPage = new HomePage(Options); Current.MainPage = new NavigationPage(new HomePage(Options));
} }
} }
private async Task ClearCacheIfNeededAsync() private async Task ClearCacheIfNeededAsync()
{ {
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey); var lastClear = await _stateService.GetLastFileCacheClearAsync();
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1) if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
{ {
var task = Task.Run(() => _deviceActionService.ClearCacheAsync()); var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
@@ -326,12 +376,12 @@ namespace Bit.App
{ {
InitializeComponent(); InitializeComponent();
SetCulture(); SetCulture();
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources); ThemeManager.SetTheme(Current.Resources);
Current.RequestedThemeChanged += (s, a) => Current.RequestedThemeChanged += (s, a) =>
{ {
UpdateTheme(); UpdateTheme();
}; };
Current.MainPage = new HomePage(); Current.MainPage = new NavigationPage(new HomePage(Options));
var mainPageTask = SetMainPageAsync(); var mainPageTask = SetMainPageAsync();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init(); ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
} }
@@ -357,17 +407,16 @@ namespace Bit.App
{ {
Device.BeginInvokeOnMainThread(() => Device.BeginInvokeOnMainThread(() =>
{ {
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources); ThemeManager.SetTheme(Current.Resources);
_messagingService.Send("updatedTheme"); _messagingService.Send("updatedTheme");
}); });
} }
private async Task LockedAsync(bool autoPromptBiometric) private async Task LockedAsync(bool autoPromptBiometric)
{ {
await _stateService.PurgeAsync();
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS) if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
{ {
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey); var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
if (vaultTimeout == 0) if (vaultTimeout == 0)
{ {
autoPromptBiometric = false; autoPromptBiometric = false;
@@ -397,7 +446,7 @@ namespace Bit.App
} }
} }
} }
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock); await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
var lockPage = new LockPage(Options, autoPromptBiometric); var lockPage = new LockPage(Options, autoPromptBiometric);
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage)); Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
} }

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="Bit.App.Controls.AccountViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="controls:AccountViewCellViewModel">
<Grid RowSpacing="0"
ColumnSpacing="0">
<Grid.Resources>
<u:InverseBoolConverter x:Key="inverseBool" />
</Grid.Resources>
<Grid
IsVisible="{Binding IsAccount}"
VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid
Grid.Row="0"
Grid.Column="0"
RowSpacing="3"
Margin="12,0,0,0"
VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="0"
Grid.Row="0"
Text="{Binding AccountView.Email}"
StyleClass="list-title"
LineBreakMode="TailTruncation" />
<Label
Grid.Column="0"
Grid.Row="1"
Text="{Binding AccountView.Hostname}"
StyleClass="list-sub"
LineBreakMode="TailTruncation" />
</Grid>
<Label
Grid.Column="1"
Grid.Row="0"
Text="{Binding AuthStatusText}"
FontSize="Small"
FontAttributes="Italic"
LineBreakMode="NoWrap"
HorizontalOptions="End"
Margin="10,0,20,0"
VerticalOptions="Center" />
</Grid>
<Grid
IsVisible="{Binding IsAccount, Converter={StaticResource inverseBool}}"
VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
VerticalOptions="Center"
Margin="12,0,0,0"
WidthRequest="{OnPlatform 14, iOS=14, Android=26}"
HeightRequest="{OnPlatform 14, iOS=14, Android=26}"
Source="plus.png"
xct:IconTintColorEffect.TintColor="{DynamicResource TextColor}"
AutomationProperties.IsInAccessibleTree="False" />
<Label
Text="Add Account"
StyleClass="list-title"
LineBreakMode="TailTruncation"
VerticalOptions="Center"
Grid.Column="1" />
</Grid>
</Grid>
</ViewCell>

View File

@@ -0,0 +1,35 @@
using Bit.Core.Models.View;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class AccountViewCell : ViewCell
{
public static readonly BindableProperty AccountProperty = BindableProperty.Create(
nameof(Account), typeof(AccountView), typeof(AccountViewCell), default(AccountView), BindingMode.OneWay);
public AccountViewCell()
{
InitializeComponent();
}
public AccountView Account
{
get => GetValue(AccountProperty) as AccountView;
set => SetValue(AccountProperty, value);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == AccountProperty.PropertyName)
{
if (Account == null)
{
return;
}
BindingContext = new AccountViewCellViewModel(Account);
}
}
}
}

View File

@@ -0,0 +1,31 @@
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.App.Controls
{
public class AccountViewCellViewModel : ExtendedViewModel
{
private AccountView _accountView;
public AccountViewCellViewModel(AccountView accountView)
{
AccountView = accountView;
}
public AccountView AccountView
{
get => _accountView;
set => SetProperty(ref _accountView, value);
}
public bool IsAccount
{
get => AccountView.IsAccount;
}
public string AuthStatusText
{
get => AccountView.AuthStatus.ToString();
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SkiaSharp;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class AvatarImageSource : StreamImageSource
{
private string _data;
public AvatarImageSource(string data = null)
{
_data = data;
}
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
{
OnLoadingStarted();
userToken.Register(CancellationTokenSource.Cancel);
var result = Draw();
OnLoadingCompleted(CancellationTokenSource.IsCancellationRequested);
return Task.FromResult(result);
}
private Stream Draw()
{
string chars = null;
string upperData = null;
if (string.IsNullOrEmpty(_data))
{
chars = "..";
}
else if (_data?.Length > 2)
{
upperData = _data.ToUpper();
chars = upperData.Substring(0, 2).ToUpper();
}
var bgColor = StringToColor(upperData);
var textColor = Color.White;
var size = 50;
var bitmap = new SKBitmap(
size * 2,
size * 2,
SKImageInfo.PlatformColorType,
SKAlphaType.Premul);
var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Transparent);
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
var radius = midX - midX / 5;
var circlePaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeJoin = SKStrokeJoin.Miter,
Color = SKColor.Parse(bgColor.ToHex())
};
canvas.DrawCircle(midX, midY, radius, circlePaint);
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
var textSize = midX / 1.3f;
var textPaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
Color = SKColor.Parse(textColor.ToHex()),
TextSize = textSize,
TextAlign = SKTextAlign.Center,
Typeface = typeface
};
var rect = new SKRect();
textPaint.MeasureText(chars, ref rect);
canvas.DrawText(chars, midX, midY + rect.Height / 2, textPaint);
return SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100).AsStream();
}
private Color StringToColor(string str)
{
if (str == null)
{
return Color.FromHex("#33ffffff");
}
var hash = 0;
for (var i = 0; i < str.Length; i++)
{
hash = str[i] + ((hash << 5) - hash);
}
var color = "#FF";
for (var i = 0; i < 3; i++)
{
var value = (hash >> (i * 8)) & 0xff;
var base16 = "00" + Convert.ToString(value, 16);
color += base16.Substring(base16.Length - 2);
}
//if (Device.RuntimePlatform == Device.iOS)
//{
// // TODO remove this once iOS ToolbarItem tint issue is solved
// return Color.FromHex("#33ffffff");
//}
return Color.FromHex(color);
}
}
}

View File

@@ -0,0 +1,9 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedToolbarItem : ToolbarItem
{
public bool UseOriginalImage { get; set; }
}
}

View File

@@ -14,7 +14,7 @@ namespace Bit.App.Pages
public class BaseChangePasswordViewModel : BaseViewModel public class BaseChangePasswordViewModel : BaseViewModel
{ {
protected readonly IPlatformUtilsService _platformUtilsService; protected readonly IPlatformUtilsService _platformUtilsService;
protected readonly IUserService _userService; protected readonly IStateService _stateService;
protected readonly IPolicyService _policyService; protected readonly IPolicyService _policyService;
protected readonly IPasswordGenerationService _passwordGenerationService; protected readonly IPasswordGenerationService _passwordGenerationService;
protected readonly II18nService _i18nService; protected readonly II18nService _i18nService;
@@ -31,7 +31,7 @@ namespace Bit.App.Pages
protected BaseChangePasswordViewModel() protected BaseChangePasswordViewModel()
{ {
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService"); _policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
_passwordGenerationService = _passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService"); ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
@@ -172,7 +172,7 @@ namespace Bit.App.Pages
private async Task<List<string>> GetPasswordStrengthUserInput() private async Task<List<string>> GetPasswordStrengthUserInput()
{ {
var email = await _userService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
List<string> userInput = null; List<string> userInput = null;
var atPosition = email.IndexOf('@'); var atPosition = email.IndexOf('@');
if (atPosition > -1) if (atPosition > -1)

View File

@@ -10,14 +10,11 @@ namespace Bit.App.Pages
public partial class EnvironmentPage : BaseContentPage public partial class EnvironmentPage : BaseContentPage
{ {
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
private readonly EnvironmentPageViewModel _vm; private readonly EnvironmentPageViewModel _vm;
public EnvironmentPage() public EnvironmentPage()
{ {
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent(); InitializeComponent();
_vm = BindingContext as EnvironmentPageViewModel; _vm = BindingContext as EnvironmentPageViewModel;
_vm.Page = this; _vm.Page = this;
@@ -35,7 +32,6 @@ namespace Bit.App.Pages
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync()); _vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
_vm.CloseAction = async () => _vm.CloseAction = async () =>
{ {
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
}; };
} }

View File

@@ -1,53 +1,107 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.HomePage" x:Class="Bit.App.Pages.HomePage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:HomeViewModel" x:DataType="pages:HomeViewModel"
Title="{Binding PageTitle}"> Title="{Binding PageTitle}">
<ContentPage.BindingContext> <ContentPage.BindingContext>
<pages:HomeViewModel /> <pages:HomeViewModel />
</ContentPage.BindingContext> </ContentPage.BindingContext>
<ContentPage.ToolbarItems> <ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" /> <controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<StackLayout Spacing="0" Padding="10, 5"> <ContentPage.Resources>
<controls:FaButton Text="&#xf013;" <ResourceDictionary>
StyleClass="btn-muted, btn-icon, btn-icon-platform" <StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
HorizontalOptions="Start" <StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
Clicked="Environment_Clicked" <Image
AutomationProperties.IsInAccessibleTree="True" x:Name="_logo"
AutomationProperties.Name="{u:I18n Options}"> Source="logo.png"
<controls:FaButton.Margin> VerticalOptions="Center" />
<OnPlatform x:TypeArguments="Thickness"> <Label Text="{u:I18n LoginOrCreateNewAccount}"
<On Platform="iOS" Value="0, 10, 0, 0" /> StyleClass="text-lg"
<On Platform="Android" Value="0" /> HorizontalTextAlignment="Center">
</OnPlatform> </Label>
</controls:FaButton.Margin> <StackLayout Spacing="5">
</controls:FaButton> <Button Text="{u:I18n LogIn}"
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20"> StyleClass="btn-primary"
<Image Clicked="LogIn_Clicked" />
x:Name="_logo" <Button Text="{u:I18n CreateAccount}"
Source="logo.png" Clicked="Register_Clicked" />
VerticalOptions="Center" /> <Button Text="{u:I18n LogInSso}"
<Label Text="{u:I18n LoginOrCreateNewAccount}" Clicked="LogInSso_Clicked" />
StyleClass="text-lg" </StackLayout>
HorizontalTextAlignment="Center"></Label> </StackLayout>
<StackLayout Spacing="5">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
<Button Text="{u:I18n CreateAccount}"
Clicked="Register_Clicked" />
<Button Text="{u:I18n LogInSso}"
Clicked="LogInSso_Clicked" />
</StackLayout> </StackLayout>
</StackLayout> </ResourceDictionary>
</StackLayout> </ContentPage.Resources>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<!-- Account Switching Overlay -->
<ContentView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
IsVisible="False"
BackgroundColor="#22000000"
Padding="0">
<StackLayout
x:Name="_accountListContainer"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}"
xct:ShadowEffect.Color="Black"
xct:ShadowEffect.Radius="10"
xct:ShadowEffect.OffsetY="3">
<ListView
x:Name="_accountListView"
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate x:DataType="view:AccountView">
<controls:AccountViewCell
Account="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView>
</AbsoluteLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -12,13 +12,10 @@ namespace Bit.App.Pages
{ {
private readonly HomeViewModel _vm; private readonly HomeViewModel _vm;
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService; private IBroadcasterService _broadcasterService;
public HomePage(AppOptions appOptions = null) public HomePage(AppOptions appOptions = null)
{ {
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", false);
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_appOptions = appOptions; _appOptions = appOptions;
InitializeComponent(); InitializeComponent();
@@ -29,6 +26,11 @@ namespace Bit.App.Pages
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync()); _vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync()); _vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
UpdateLogo(); UpdateLogo();
if (!_appOptions?.IosExtension ?? false)
{
ToolbarItems.Remove(_closeItem);
}
} }
public async Task DismissRegisterPageAndLogInAsync(string email) public async Task DismissRegisterPageAndLogInAsync(string email)
@@ -37,10 +39,18 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions))); await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
} }
protected override void OnAppearing() protected override async void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
_messagingService.Send("showStatusBar", false); _mainContent.Content = _mainLayout;
if (await ShowAccountSwitcherAsync())
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
else
{
ToolbarItems.Remove(_accountAvatar);
}
_broadcasterService.Subscribe(nameof(HomePage), async (message) => _broadcasterService.Subscribe(nameof(HomePage), async (message) =>
{ {
if (message.Command == "updatedTheme") if (message.Command == "updatedTheme")
@@ -127,5 +137,23 @@ namespace Bit.App.Pages
var page = new EnvironmentPage(); var page = new EnvironmentPage();
await Navigation.PushModalAsync(new NavigationPage(page)); await Navigation.PushModalAsync(new NavigationPage(page));
} }
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
}
else
{
await RefreshAccountViewsAsync(_accountListView, false);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
}
} }
} }

View File

@@ -1,15 +1,27 @@
using System; using System;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class HomeViewModel : BaseViewModel public class HomeViewModel : BaseViewModel
{ {
private readonly IStateService _stateService;
public HomeViewModel() public HomeViewModel()
{ {
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
PageTitle = AppResources.Bitwarden; PageTitle = AppResources.Bitwarden;
} }
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public Action StartLoginAction { get; set; } public Action StartLoginAction { get; set; }
public Action StartRegisterAction { get; set; } public Action StartRegisterAction { get; set; }
public Action StartSsoLoginAction { get; set; } public Action StartSsoLoginAction { get; set; }

View File

@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LockPage" x:Class="Bit.App.Pages.LockPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LockPageViewModel" x:DataType="pages:LockPageViewModel"
Title="{Binding PageTitle}"> Title="{Binding PageTitle}">
@@ -13,6 +15,19 @@
<pages:LockPageViewModel /> <pages:LockPageViewModel />
</ContentPage.BindingContext> </ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources> <ContentPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" /> <u:InverseBoolConverter x:Key="inverseBool" />
@@ -25,119 +40,162 @@
x:Name="_logOut" x:Name="_logOut"
Clicked="LogOut_Clicked" Clicked="LogOut_Clicked"
Order="Secondary"/> Order="Secondary"/>
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<Grid
StyleClass="box-row"
IsVisible="{Binding PinLock}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n PIN}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_pin"
Text="{Binding Pin}"
StyleClass="box-value"
Keyboard="Numeric"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Grid
x:Name="_passwordGrid"
StyleClass="box-row"
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<StackLayout
StyleClass="box-row"
Padding="0, 10, 0, 0">
<Label
Text="{Binding LockedVerifyText}"
StyleClass="box-footer-label" />
<Label
Text="{Binding LoggedInAsText}"
StyleClass="box-footer-label"
Margin="0, 10, 0, 0" />
</StackLayout>
</StackLayout>
<StackLayout Padding="10, 0">
<Label
Text="{u:I18n BiometricInvalidated}"
StyleClass="box-footer-label,text-danger,text-bold"
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
IsVisible="{Binding BiometricButtonVisible}">
</Button>
<Button
x:Name="_unlockButton"
Text="{u:I18n Unlock}"
StyleClass="btn-primary"
Clicked="Unlock_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
<ContentPage.ToolbarItems> <AbsoluteLayout
</ContentPage.ToolbarItems> x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<ScrollView> <!-- Account Switching Overlay -->
<StackLayout Spacing="20"> <ContentView
<StackLayout StyleClass="box"> x:Name="_accountListOverlay"
<Grid AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
StyleClass="box-row" AbsoluteLayout.LayoutFlags="All"
IsVisible="{Binding PinLock}" IsVisible="False"
Padding="0, 10, 0, 0"> BackgroundColor="#22000000"
<Grid.RowDefinitions> Padding="0">
<RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <StackLayout
</Grid.RowDefinitions> x:Name="_accountListContainer"
<Grid.ColumnDefinitions> VerticalOptions="StartAndExpand"
<ColumnDefinition Width="*" /> HorizontalOptions="FillAndExpand"
<ColumnDefinition Width="Auto" /> BackgroundColor="{DynamicResource BackgroundColor}"
</Grid.ColumnDefinitions> xct:ShadowEffect.Color="Black"
<Label xct:ShadowEffect.Radius="10"
Text="{u:I18n PIN}" xct:ShadowEffect.OffsetY="3">
StyleClass="box-label" <ListView
Grid.Row="0" x:Name="_accountListView"
Grid.Column="0" /> ItemsSource="{Binding AccountViews}"
<controls:MonoEntry ItemSelected="AccountRow_Selected"
x:Name="_pin" BackgroundColor="Transparent"
Text="{Binding Pin}" VerticalOptions="FillAndExpand"
StyleClass="box-value" RowHeight="60">
Keyboard="Numeric" <ListView.ItemTemplate>
IsSpellCheckEnabled="False" <DataTemplate x:DataType="view:AccountView">
IsTextPredictionEnabled="False" <controls:AccountViewCell
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}" Account="{Binding .}" />
Grid.Row="1" </DataTemplate>
Grid.Column="0" </ListView.ItemTemplate>
ReturnType="Go" </ListView>
ReturnCommand="{Binding SubmitCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Grid
x:Name="_passwordGrid"
StyleClass="box-row"
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<StackLayout
StyleClass="box-row"
Padding="0, 10, 0, 0">
<Label
Text="{Binding LockedVerifyText}"
StyleClass="box-footer-label" />
<Label
Text="{Binding LoggedInAsText}"
StyleClass="box-footer-label"
Margin="0, 10, 0, 0" />
</StackLayout>
</StackLayout> </StackLayout>
<StackLayout Padding="10, 0"> </ContentView>
<Label </AbsoluteLayout>
Text="{u:I18n BiometricInvalidated}"
StyleClass="box-footer-label,text-danger,text-bold"
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
IsVisible="{Binding BiometricButtonVisible}"></Button>
<Button
x:Name="_unlockButton"
Text="{u:I18n Unlock}"
StyleClass="btn-primary"
Clicked="Unlock_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -1,7 +1,5 @@
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Utilities; using Bit.App.Utilities;
@@ -63,6 +61,15 @@ namespace Bit.App.Pages
return; return;
} }
_appeared = true; _appeared = true;
_mainContent.Content = _mainLayout;
if (await ShowAccountSwitcherAsync())
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
else
{
ToolbarItems.Remove(_accountAvatar);
}
await _vm.InitAsync(); await _vm.InitAsync();
if (!_vm.BiometricLock) if (!_vm.BiometricLock)
{ {
@@ -147,5 +154,23 @@ namespace Bit.App.Pages
Application.Current.MainPage = new TabsPage(_appOptions, previousPage); Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
} }
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
}
else
{
await RefreshAccountViewsAsync(_accountListView, false);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
}
} }
} }

View File

@@ -1,14 +1,14 @@
using System;
using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Models.Request;
using Bit.Core.Models.View;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -20,10 +20,7 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IStorageService _storageService;
private readonly IUserService _userService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IStorageService _secureStorageService;
private readonly IEnvironmentService _environmentService; private readonly IEnvironmentService _environmentService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IBiometricService _biometricService; private readonly IBiometricService _biometricService;
@@ -48,10 +45,7 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService"); _cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService"); _biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
@@ -119,6 +113,11 @@ namespace Bit.App.Pages
set => SetProperty(ref _lockedVerifyText, value); set => SetProperty(ref _lockedVerifyText, value);
} }
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public Command SubmitCommand { get; } public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? "" : ""; public string ShowPasswordIcon => ShowPassword ? "" : "";
@@ -129,7 +128,7 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync(); _pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2; PinLock = (_pinSet.Item1 && _stateService.GetPinProtectedAsync() != null) || _pinSet.Item2;
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync(); BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
// Users with key connector and without biometric or pin has no MP to unlock with // Users with key connector and without biometric or pin has no MP to unlock with
@@ -138,7 +137,7 @@ namespace Bit.App.Pages
{ {
await _vaultTimeoutService.LogOutAsync(); await _vaultTimeoutService.LogOutAsync();
} }
_email = await _userService.GetEmailAsync(); _email = await _stateService.GetEmailAsync();
var webVault = _environmentService.GetWebVaultUrl(); var webVault = _environmentService.GetWebVaultUrl();
if (string.IsNullOrWhiteSpace(webVault)) if (string.IsNullOrWhiteSpace(webVault))
{ {
@@ -203,8 +202,8 @@ namespace Bit.App.Pages
} }
ShowPassword = false; ShowPassword = false;
var kdf = await _userService.GetKdfAsync(); var kdf = await _stateService.GetKdfTypeAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync(); var kdfIterations = await _stateService.GetKdfIterationsAsync();
if (PinLock) if (PinLock)
{ {
@@ -215,9 +214,9 @@ namespace Bit.App.Pages
{ {
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000), kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
_vaultTimeoutService.PinProtectedKey); await _stateService.GetPinProtectedCachedAsync());
var encKey = await _cryptoService.GetEncKeyAsync(key); var encKey = await _cryptoService.GetEncKeyAsync(key);
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin); var protectedPin = await _stateService.GetProtectedPinAsync();
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
failed = decPin != Pin; failed = decPin != Pin;
if (!failed) if (!failed)
@@ -286,12 +285,12 @@ namespace Bit.App.Pages
{ {
if (_pinSet.Item1) if (_pinSet.Item1)
{ {
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin); var protectedPin = await _stateService.GetProtectedPinAsync();
var encKey = await _cryptoService.GetEncKeyAsync(key); var encKey = await _cryptoService.GetEncKeyAsync(key);
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000)); kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey); await _stateService.SetPinProtectedCachedAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
} }
MasterPassword = string.Empty; MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
@@ -357,7 +356,7 @@ namespace Bit.App.Pages
page.MasterPasswordEntry.Focus(); page.MasterPasswordEntry.Focus();
} }
}); });
_vaultTimeoutService.BiometricLocked = !success; _stateService.BiometricLocked = !success;
if (success) if (success)
{ {
await DoContinueAsync(); await DoContinueAsync();
@@ -376,9 +375,7 @@ namespace Bit.App.Pages
private async Task DoContinueAsync() private async Task DoContinueAsync()
{ {
_vaultTimeoutService.BiometricLocked = false; _stateService.BiometricLocked = false;
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
_messagingService.Send("unlocked"); _messagingService.Send("unlocked");
UnlockedAction?.Invoke(); UnlockedAction?.Invoke();
} }

View File

@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginPage" x:Class="Bit.App.Pages.LoginPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LoginPageViewModel" x:DataType="pages:LoginPageViewModel"
Title="{Binding PageTitle}"> Title="{Binding PageTitle}">
@@ -13,6 +15,21 @@
<pages:LoginPageViewModel /> <pages:LoginPageViewModel />
</ContentPage.BindingContext> </ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ContentPage.Resources> <ContentPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" /> <u:InverseBoolConverter x:Key="inverseBool" />
@@ -25,68 +42,109 @@
x:Name="_getPasswordHint" x:Name="_getPasswordHint"
Clicked="Hint_Clicked" Clicked="Hint_Clicked"
Order="Secondary"/> Order="Secondary"/>
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row">
<Label
Text="{u:I18n EmailAddress}"
StyleClass="box-label" />
<Entry
x:Name="_email"
Text="{Binding Email}"
Keyboard="Email"
StyleClass="box-value" />
</StackLayout>
<Grid StyleClass="box-row">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding LogInCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<ContentPage.ToolbarItems> <!-- Account Switching Overlay -->
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" /> <ContentView
</ContentPage.ToolbarItems> x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
IsVisible="False"
BackgroundColor="#22000000"
Padding="0">
<ScrollView> <StackLayout
<StackLayout Spacing="20"> x:Name="_accountListContainer"
<StackLayout StyleClass="box"> VerticalOptions="StartAndExpand"
<StackLayout StyleClass="box-row"> HorizontalOptions="FillAndExpand"
<Label BackgroundColor="{DynamicResource BackgroundColor}"
Text="{u:I18n EmailAddress}" xct:ShadowEffect.Color="Black"
StyleClass="box-label" /> xct:ShadowEffect.Radius="10"
<Entry xct:ShadowEffect.OffsetY="3">
x:Name="_email" <ListView
Text="{Binding Email}" x:Name="_accountListView"
Keyboard="Email" ItemsSource="{Binding AccountViews}"
StyleClass="box-value" /> ItemSelected="AccountRow_Selected"
</StackLayout> BackgroundColor="Transparent"
<Grid StyleClass="box-row"> VerticalOptions="FillAndExpand"
<Grid.RowDefinitions> RowHeight="60">
<RowDefinition Height="Auto" /> <ListView.ItemTemplate>
<RowDefinition Height="*" /> <DataTemplate x:DataType="view:AccountView">
</Grid.RowDefinitions> <controls:AccountViewCell
<Grid.ColumnDefinitions> Account="{Binding .}" />
<ColumnDefinition Width="*" /> </DataTemplate>
<ColumnDefinition Width="Auto" /> </ListView.ItemTemplate>
</Grid.ColumnDefinitions> </ListView>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding LogInCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
</StackLayout> </StackLayout>
<StackLayout Padding="10, 0"> </ContentView>
<Button Text="{u:I18n LogIn}" </AbsoluteLayout>
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -1,7 +1,5 @@
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Utilities; using Bit.App.Utilities;
@@ -11,8 +9,6 @@ namespace Bit.App.Pages
{ {
public partial class LoginPage : BaseContentPage public partial class LoginPage : BaseContentPage
{ {
private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService;
private readonly LoginPageViewModel _vm; private readonly LoginPageViewModel _vm;
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
@@ -20,9 +16,6 @@ namespace Bit.App.Pages
public LoginPage(string email = null, AppOptions appOptions = null) public LoginPage(string email = null, AppOptions appOptions = null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
_appOptions = appOptions; _appOptions = appOptions;
InitializeComponent(); InitializeComponent();
_vm = BindingContext as LoginPageViewModel; _vm = BindingContext as LoginPageViewModel;
@@ -33,7 +26,6 @@ namespace Bit.App.Pages
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync()); () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () => _vm.CloseAction = async () =>
{ {
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
}; };
_vm.Email = email; _vm.Email = email;
@@ -54,6 +46,11 @@ namespace Bit.App.Pages
{ {
ToolbarItems.Add(_getPasswordHint); ToolbarItems.Add(_getPasswordHint);
} }
if (!_appOptions?.IosExtension ?? false)
{
ToolbarItems.Remove(_closeItem);
}
} }
public Entry MasterPasswordEntry { get; set; } public Entry MasterPasswordEntry { get; set; }
@@ -61,6 +58,15 @@ namespace Bit.App.Pages
protected override async void OnAppearing() protected override async void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
_mainContent.Content = _mainLayout;
if (await ShowAccountSwitcherAsync())
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
else
{
ToolbarItems.Remove(_accountAvatar);
}
await _vm.InitAsync(); await _vm.InitAsync();
if (!_inputFocused) if (!_inputFocused)
{ {
@@ -130,5 +136,23 @@ namespace Bit.App.Pages
var page = new UpdateTempPasswordPage(); var page = new UpdateTempPasswordPage();
await Navigation.PushModalAsync(new NavigationPage(page)); await Navigation.PushModalAsync(new NavigationPage(page));
} }
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
}
else
{
await RefreshAccountViewsAsync(_accountListView, false);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
}
} }
} }

View File

@@ -1,25 +1,21 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Models.View;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class LoginPageViewModel : CaptchaProtectedViewModel public class LoginPageViewModel : CaptchaProtectedViewModel
{ {
private const string Keys_RememberedEmail = "rememberedEmail";
private const string Keys_RememberEmail = "rememberEmail";
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IEnvironmentService _environmentService; private readonly IEnvironmentService _environmentService;
@@ -34,7 +30,6 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_authService = ServiceContainer.Resolve<IAuthService>("authService"); _authService = ServiceContainer.Resolve<IAuthService>("authService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
@@ -67,6 +62,11 @@ namespace Bit.App.Pages
set => SetProperty(ref _masterPassword, value); set => SetProperty(ref _masterPassword, value);
} }
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public Command LogInCommand { get; } public Command LogInCommand { get; }
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? "" : ""; public string ShowPasswordIcon => ShowPassword ? "" : "";
@@ -85,9 +85,9 @@ namespace Bit.App.Pages
{ {
if (string.IsNullOrWhiteSpace(Email)) if (string.IsNullOrWhiteSpace(Email))
{ {
Email = await _storageService.GetAsync<string>(Keys_RememberedEmail); Email = await _stateService.GetRememberedEmailAsync();
} }
var rememberEmail = await _storageService.GetAsync<bool?>(Keys_RememberEmail); var rememberEmail = await _stateService.GetRememberEmailAsync();
RememberEmail = rememberEmail.GetValueOrDefault(true); RememberEmail = rememberEmail.GetValueOrDefault(true);
} }
@@ -131,11 +131,11 @@ namespace Bit.App.Pages
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken); var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
if (RememberEmail) if (RememberEmail)
{ {
await _storageService.SaveAsync(Keys_RememberedEmail, Email); await _stateService.SetRememberedEmailAsync(Email);
} }
else else
{ {
await _storageService.RemoveAsync(Keys_RememberedEmail); await _stateService.SetRememberedEmailAsync(null);
} }
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
@@ -163,8 +163,6 @@ namespace Bit.App.Pages
} }
else else
{ {
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
var task = Task.Run(async () => await _syncService.FullSyncAsync(true)); var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
LogInSuccessAction?.Invoke(); LogInSuccessAction?.Invoke();
} }

View File

@@ -10,8 +10,6 @@ namespace Bit.App.Pages
{ {
public partial class LoginSsoPage : BaseContentPage public partial class LoginSsoPage : BaseContentPage
{ {
private readonly IStorageService _storageService;
private readonly IMessagingService _messagingService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly LoginSsoPageViewModel _vm; private readonly LoginSsoPageViewModel _vm;
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
@@ -20,10 +18,7 @@ namespace Bit.App.Pages
public LoginSsoPage(AppOptions appOptions = null) public LoginSsoPage(AppOptions appOptions = null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_messagingService.Send("showStatusBar", true);
_appOptions = appOptions; _appOptions = appOptions;
InitializeComponent(); InitializeComponent();
_vm = BindingContext as LoginSsoPageViewModel; _vm = BindingContext as LoginSsoPageViewModel;
@@ -36,7 +31,6 @@ namespace Bit.App.Pages
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync()); () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () => _vm.CloseAction = async () =>
{ {
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
}; };
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)

View File

@@ -1,6 +1,5 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
@@ -16,16 +15,12 @@ namespace Bit.App.Pages
{ {
public class LoginSsoPageViewModel : BaseViewModel public class LoginSsoPageViewModel : BaseViewModel
{ {
private const string Keys_RememberedOrgIdentifier = "rememberedOrgIdentifier";
private const string Keys_RememberOrgIdentifier = "rememberOrgIdentifier";
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IPasswordGenerationService _passwordGenerationService; private readonly IPasswordGenerationService _passwordGenerationService;
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
@@ -40,7 +35,6 @@ namespace Bit.App.Pages
_passwordGenerationService = _passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService"); ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService"); _cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
@@ -66,9 +60,9 @@ namespace Bit.App.Pages
{ {
if (string.IsNullOrWhiteSpace(OrgIdentifier)) if (string.IsNullOrWhiteSpace(OrgIdentifier))
{ {
OrgIdentifier = await _storageService.GetAsync<string>(Keys_RememberedOrgIdentifier); OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
} }
var rememberOrgIdentifier = await _storageService.GetAsync<bool?>(Keys_RememberOrgIdentifier); var rememberOrgIdentifier = await _stateService.GetRememberOrgIdentifierAsync();
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true); RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
} }
@@ -172,11 +166,11 @@ namespace Bit.App.Pages
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
if (RememberOrgIdentifier) if (RememberOrgIdentifier)
{ {
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier); await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
} }
else else
{ {
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier); await _stateService.SetRememberedOrgIdentifierAsync(null);
} }
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
if (response.TwoFactor) if (response.TwoFactor)
@@ -193,8 +187,6 @@ namespace Bit.App.Pages
} }
else else
{ {
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
var task = Task.Run(async () => await _syncService.FullSyncAsync(true)); var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
SsoAuthSuccessAction?.Invoke(); SsoAuthSuccessAction?.Invoke();
} }

View File

@@ -1,6 +1,4 @@
using Bit.Core.Abstractions; using System;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
@@ -8,22 +6,18 @@ namespace Bit.App.Pages
{ {
public partial class RegisterPage : BaseContentPage public partial class RegisterPage : BaseContentPage
{ {
private readonly IMessagingService _messagingService;
private readonly RegisterPageViewModel _vm; private readonly RegisterPageViewModel _vm;
private bool _inputFocused; private bool _inputFocused;
public RegisterPage(HomePage homePage) public RegisterPage(HomePage homePage)
{ {
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent(); InitializeComponent();
_vm = BindingContext as RegisterPageViewModel; _vm = BindingContext as RegisterPageViewModel;
_vm.Page = this; _vm.Page = this;
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage)); _vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
_vm.CloseAction = async () => _vm.CloseAction = async () =>
{ {
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
}; };
MasterPasswordEntry = _masterPassword; MasterPasswordEntry = _masterPassword;

View File

@@ -1,6 +1,4 @@
using Bit.Core.Abstractions; using System;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Utilities; using Bit.App.Utilities;
@@ -10,14 +8,11 @@ namespace Bit.App.Pages
{ {
public partial class SetPasswordPage : BaseContentPage public partial class SetPasswordPage : BaseContentPage
{ {
private readonly IMessagingService _messagingService;
private readonly SetPasswordPageViewModel _vm; private readonly SetPasswordPageViewModel _vm;
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null) public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
{ {
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
_appOptions = appOptions; _appOptions = appOptions;
InitializeComponent(); InitializeComponent();
_vm = BindingContext as SetPasswordPageViewModel; _vm = BindingContext as SetPasswordPageViewModel;
@@ -26,7 +21,6 @@ namespace Bit.App.Pages
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync()); () => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
_vm.CloseAction = async () => _vm.CloseAction = async () =>
{ {
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
}; };
_vm.OrgIdentifier = orgIdentifier; _vm.OrgIdentifier = orgIdentifier;

View File

@@ -7,7 +7,6 @@ using Bit.Core.Models.Request;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private readonly IPasswordGenerationService _passwordGenerationService; private readonly IPasswordGenerationService _passwordGenerationService;
private readonly II18nService _i18nService; private readonly II18nService _i18nService;
@@ -40,7 +39,7 @@ namespace Bit.App.Pages
_apiService = ServiceContainer.Resolve<IApiService>("apiService"); _apiService = ServiceContainer.Resolve<IApiService>("apiService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService"); _cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService"); _policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
_passwordGenerationService = _passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService"); ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
@@ -159,7 +158,7 @@ namespace Bit.App.Pages
var kdf = KdfType.PBKDF2_SHA256; var kdf = KdfType.PBKDF2_SHA256;
var kdfIterations = 100000; var kdfIterations = 100000;
var email = await _userService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations); var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization); var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization); var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
@@ -196,8 +195,8 @@ namespace Bit.App.Pages
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount); await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
// Set Password and relevant information // Set Password and relevant information
await _apiService.SetPasswordAsync(request); await _apiService.SetPasswordAsync(request);
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(), await _stateService.SetKdfTypeAsync(kdf);
await _userService.GetEmailAsync(), kdf, kdfIterations); await _stateService.SetKdfIterationsAsync(kdfIterations);
await _cryptoService.SetKeyAsync(key); await _cryptoService.SetKeyAsync(key);
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash); await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString); await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
@@ -216,7 +215,7 @@ namespace Bit.App.Pages
{ {
ResetPasswordKey = encryptedKey.EncryptedString ResetPasswordKey = encryptedKey.EncryptedString
}; };
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
// Enroll user // Enroll user
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest); await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
} }
@@ -289,7 +288,7 @@ namespace Bit.App.Pages
private async Task<List<string>> GetPasswordStrengthUserInput() private async Task<List<string>> GetPasswordStrengthUserInput()
{ {
var email = await _userService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
List<string> userInput = null; List<string> userInput = null;
var atPosition = email.IndexOf('@'); var atPosition = email.IndexOf('@');
if (atPosition > -1) if (atPosition > -1)

View File

@@ -14,8 +14,6 @@ namespace Bit.App.Pages
{ {
private readonly IBroadcasterService _broadcasterService; private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
private TwoFactorPageViewModel _vm; private TwoFactorPageViewModel _vm;
@@ -30,10 +28,8 @@ namespace Bit.App.Pages
_authingWithSso = authingWithSso ?? false; _authingWithSso = authingWithSso ?? false;
_appOptions = appOptions; _appOptions = appOptions;
_orgIdentifier = orgIdentifier; _orgIdentifier = orgIdentifier;
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_vm = BindingContext as TwoFactorPageViewModel; _vm = BindingContext as TwoFactorPageViewModel;
_vm.Page = this; _vm.Page = this;
_vm.StartSetPasswordAction = () => _vm.StartSetPasswordAction = () =>

View File

@@ -1,6 +1,5 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
@@ -23,7 +22,6 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IStorageService _storageService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IEnvironmentService _environmentService; private readonly IEnvironmentService _environmentService;
@@ -43,7 +41,6 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_authService = ServiceContainer.Resolve<IAuthService>("authService"); _authService = ServiceContainer.Resolve<IAuthService>("authService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService"); _apiService = ServiceContainer.Resolve<IApiService>("apiService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
@@ -303,8 +300,6 @@ namespace Bit.App.Pages
} }
else else
{ {
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
TwoFactorAuthSuccessAction?.Invoke(); TwoFactorAuthSuccessAction?.Invoke();
} }
} }

View File

@@ -19,9 +19,6 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
// Service Use
_messagingService.Send("showStatusBar", true);
// Binding // Binding
InitializeComponent(); InitializeComponent();
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks); _pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);

View File

@@ -43,9 +43,9 @@ namespace Bit.App.Pages
} }
// Retrieve details for key generation // Retrieve details for key generation
var kdf = await _userService.GetKdfAsync(); var kdf = await _stateService.GetKdfTypeAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync(); var kdfIterations = await _stateService.GetKdfIterationsAsync();
var email = await _userService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
// Create new key and hash new password // Create new key and hash new password
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations); var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);

View File

@@ -1,10 +1,11 @@
using Bit.Core; using Bit.Core.Abstractions;
using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Enums;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration; using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific; using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
@@ -13,8 +14,9 @@ namespace Bit.App.Pages
{ {
public class BaseContentPage : ContentPage public class BaseContentPage : ContentPage
{ {
private IStorageService _storageService; private IStateService _stateService;
private IDeviceActionService _deviceActionService; private IDeviceActionService _deviceActionService;
private IMessagingService _messagingService;
protected int ShowModalAnimationDelay = 400; protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100; protected int ShowPageAnimationDelay = 100;
@@ -30,10 +32,10 @@ namespace Bit.App.Pages
public DateTime? LastPageAction { get; set; } public DateTime? LastPageAction { get; set; }
protected override void OnAppearing() protected async override void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
SaveActivity(); await SaveActivity();
} }
public bool DoOnce(Action action = null, int milliseconds = 1000) public bool DoOnce(Action action = null, int milliseconds = 1000)
@@ -106,22 +108,121 @@ namespace Bit.App.Pages
}); });
} }
protected async Task<bool> ShowAccountSwitcherAsync()
{
return await _stateService.HasMultipleAccountsAsync();
}
protected async Task RefreshAccountViewsAsync(Xamarin.Forms.ListView accountListView, bool allowAddAccountRow)
{
await _stateService.RefreshAccountViewsAsync(allowAddAccountRow);
// Property change trigger on account listview is yielding inconsistent results, using a hammer instead
accountListView.ItemsSource = null;
accountListView.ItemsSource = _stateService.AccountViews;
}
protected async Task<AvatarImageSource> GetAvatarImageSourceAsync(bool useCurrentActiveAccount = true)
{
return new AvatarImageSource(useCurrentActiveAccount ? await _stateService.GetEmailAsync() : null);
}
protected async Task ShowAccountListAsync(bool isVisible, View listContainer, View overlay, View fab = null)
{
Device.BeginInvokeOnMainThread(async () =>
{
// Not all animations are awaited. This is intentional to allow multiple simultaneous animations.
if (isVisible)
{
// start listView in default (off-screen) position
await listContainer.TranslateTo(0, listContainer.Height * -1, 0);
// set overlay opacity to zero before making visible and start fade-in
overlay.Opacity = 0;
overlay.IsVisible = true;
overlay.FadeTo(1, 100);
if (Device.RuntimePlatform == Device.Android && fab != null)
{
// start fab fade-out
fab.FadeTo(0, 200);
}
// slide account list into view
await listContainer.TranslateTo(0, 0, 200, Easing.SinOut);
}
else
{
// start overlay fade-out
overlay.FadeTo(0, 200);
if (Device.RuntimePlatform == Device.Android && fab != null)
{
// start fab fade-in
fab.FadeTo(1, 200);
}
// slide account list out of view
await listContainer.TranslateTo(0, listContainer.Height * -1, 200, Easing.SinIn);
// remove overlay
overlay.IsVisible = false;
}
});
}
protected async Task AccountRowSelectedAsync(object sender, SelectedItemChangedEventArgs e, View listContainer,
View overlay, View fab = null, bool? allowActiveAccountSelection = false)
{
if (!DoOnce())
{
return;
}
if (!(e.SelectedItem is AccountViewCellViewModel item))
{
return;
}
((Xamarin.Forms.ListView)sender).SelectedItem = null;
await Task.Delay(100);
await ShowAccountListAsync(false, listContainer, overlay, fab);
if (item.AccountView.IsAccount)
{
if (item.AccountView.AuthStatus != AuthenticationStatus.Active)
{
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
_messagingService.Send("switchedAccount");
}
else if (allowActiveAccountSelection ?? false)
{
_messagingService.Send("switchedAccount");
}
}
else
{
_messagingService.Send("addAccount");
}
}
private void SetServices() private void SetServices()
{ {
if (_storageService == null) if (_stateService == null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
} }
if (_deviceActionService == null) if (_deviceActionService == null)
{ {
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
} }
if (_messagingService == null)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
}
} }
private void SaveActivity() private async Task SaveActivity()
{ {
SetServices(); SetServices();
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime()); await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using Bit.Core.Utilities; using Bit.App.Controls;
using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -6,6 +7,7 @@ namespace Bit.App.Pages
public abstract class BaseViewModel : ExtendedViewModel public abstract class BaseViewModel : ExtendedViewModel
{ {
private string _pageTitle = string.Empty; private string _pageTitle = string.Empty;
private AvatarImageSource _avatar;
public string PageTitle public string PageTitle
{ {
@@ -13,6 +15,12 @@ namespace Bit.App.Pages
set => SetProperty(ref _pageTitle, value); set => SetProperty(ref _pageTitle, value);
} }
public AvatarImageSource AvatarImageSource
{
get => _avatar ?? new AvatarImageSource();
set => SetProperty(ref _avatar, value);
}
public ContentPage Page { get; set; } public ContentPage Page { get; set; }
} }
} }

View File

@@ -19,7 +19,7 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly ISendService _sendService; private readonly ISendService _sendService;
private bool _sendEnabled; private bool _sendEnabled;
private bool _canAccessPremium; private bool _canAccessPremium;
@@ -51,7 +51,7 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_sendService = ServiceContainer.Resolve<ISendService>("sendService"); _sendService = ServiceContainer.Resolve<ISendService>("sendService");
TogglePasswordCommand = new Command(TogglePassword); TogglePasswordCommand = new Command(TogglePassword);
@@ -220,8 +220,8 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend; PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
_canAccessPremium = await _userService.CanAccessPremiumAsync(); _canAccessPremium = await _stateService.CanAccessPremiumAsync();
_emailVerified = await _userService.GetEmailVerifiedAsync(); _emailVerified = await _stateService.GetEmailVerifiedAsync();
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync(); SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync(); DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail; SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;

View File

@@ -32,21 +32,19 @@ namespace Bit.App.Pages
private readonly ISendService _sendService; private readonly ISendService _sendService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStorageService _storageService;
public SendGroupingsPageViewModel() public SendGroupingsPageViewModel()
{ {
_sendService = ServiceContainer.Resolve<ISendService>("sendService"); _sendService = ServiceContainer.Resolve<ISendService>("sendService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
Loading = true; Loading = true;
PageTitle = AppResources.Send; PageTitle = AppResources.Send;
@@ -116,7 +114,7 @@ namespace Bit.App.Pages
{ {
return; return;
} }
var authed = await _userService.IsAuthenticatedAsync(); var authed = await _stateService.IsAuthenticatedAsync();
if (!authed) if (!authed)
{ {
return; return;
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
{ {
return; return;
} }
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing) if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{ {
SyncRefreshing = true; SyncRefreshing = true;
await _syncService.FullSyncAsync(false); await _syncService.FullSyncAsync(false);

View File

@@ -2,7 +2,6 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Services; using Bit.App.Services;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -11,7 +10,7 @@ namespace Bit.App.Pages
public class AutofillServicesPageViewModel : BaseViewModel public class AutofillServicesPageViewModel : BaseViewModel
{ {
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly MobileI18nService _i18nService; private readonly MobileI18nService _i18nService;
private bool _autofillServiceToggled; private bool _autofillServiceToggled;
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
public AutofillServicesPageViewModel() public AutofillServicesPageViewModel()
{ {
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService; _i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
PageTitle = AppResources.AutofillServices; PageTitle = AppResources.AutofillServices;
} }
@@ -152,7 +151,7 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true; InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
_inited = true; _inited = true;
} }
@@ -202,7 +201,7 @@ namespace Bit.App.Pages
{ {
if (_inited) if (_inited)
{ {
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled); await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
} }
} }
} }

View File

@@ -7,12 +7,8 @@ namespace Bit.App.Pages
{ {
public class ExtensionPageViewModel : BaseViewModel public class ExtensionPageViewModel : BaseViewModel
{ {
private const string StartedKey = "appExtensionStarted";
private const string ActivatedKey = "appExtensionActivated";
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly IPlatformUtilsService _platformUtilsService;
private bool _started; private bool _started;
private bool _activated; private bool _activated;
@@ -20,8 +16,7 @@ namespace Bit.App.Pages
public ExtensionPageViewModel() public ExtensionPageViewModel()
{ {
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
PageTitle = AppResources.AppExtension; PageTitle = AppResources.AppExtension;
} }
@@ -52,8 +47,8 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
var started = await _storageService.GetAsync<bool?>(StartedKey); var started = await _stateService.GetAppExtensionStartedAsync();
var activated = await _storageService.GetAsync<bool?>(ActivatedKey); var activated = await _stateService.GetAppExtensionActivatedAsync();
Started = started.GetValueOrDefault(); Started = started.GetValueOrDefault();
Activated = activated.GetValueOrDefault(); Activated = activated.GetValueOrDefault();
} }

View File

@@ -1,5 +1,4 @@
using Bit.App.Abstractions; using Bit.App.Resources;
using Bit.App.Resources;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
@@ -13,9 +12,6 @@ namespace Bit.App.Pages
{ {
public class OptionsPageViewModel : BaseViewModel public class OptionsPageViewModel : BaseViewModel
{ {
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStorageService _storageService;
private readonly ITotpService _totpService; private readonly ITotpService _totpService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
@@ -34,9 +30,6 @@ namespace Bit.App.Pages
public OptionsPageViewModel() public OptionsPageViewModel()
{ {
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_totpService = ServiceContainer.Resolve<ITotpService>("totpService"); _totpService = ServiceContainer.Resolve<ITotpService>("totpService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
@@ -166,19 +159,17 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
AutofillDisableSavePrompt = (await _storageService.GetAsync<bool?>( AutofillDisableSavePrompt = (await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
Constants.AutofillDisableSavePromptKey)).GetValueOrDefault(); var blacklistedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
var blacklistedUrisList = await _storageService.GetAsync<List<string>>(
Constants.AutofillBlacklistedUrisKey);
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null; AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync()); DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
DisableFavicon = (await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey)).GetValueOrDefault(); DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
var theme = await _storageService.GetAsync<string>(Constants.ThemeKey); var theme = await _stateService.GetThemeAsync();
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme); ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
var defaultUriMatch = await _storageService.GetAsync<int?>(Constants.DefaultUriMatch); var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
UriMatchSelectedIndex = defaultUriMatch == null ? 0 : UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch); UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
var clearClipboard = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey); var clearClipboard = await _stateService.GetClearClipboardAsync();
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard); ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
_inited = true; _inited = true;
} }
@@ -187,7 +178,7 @@ namespace Bit.App.Pages
{ {
if (_inited) if (_inited)
{ {
await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy); await _stateService.SetDisableAutoTotpCopyAsync(DisableAutoTotpCopy);
} }
} }
@@ -195,8 +186,7 @@ namespace Bit.App.Pages
{ {
if (_inited) if (_inited)
{ {
await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon); await _stateService.SetDisableFaviconAsync(DisableFavicon);
await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
} }
} }
@@ -204,8 +194,7 @@ namespace Bit.App.Pages
{ {
if (_inited && ClearClipboardSelectedIndex > -1) if (_inited && ClearClipboardSelectedIndex > -1)
{ {
await _storageService.SaveAsync(Constants.ClearClipboardKey, await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
} }
} }
@@ -214,8 +203,8 @@ namespace Bit.App.Pages
if (_inited && ThemeSelectedIndex > -1) if (_inited && ThemeSelectedIndex > -1)
{ {
var theme = ThemeOptions[ThemeSelectedIndex].Key; var theme = ThemeOptions[ThemeSelectedIndex].Key;
await _storageService.SaveAsync(Constants.ThemeKey, theme); await _stateService.SetThemeAsync(theme);
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Application.Current.Resources); ThemeManager.SetTheme(Application.Current.Resources);
_messagingService.Send("updatedTheme"); _messagingService.Send("updatedTheme");
} }
} }
@@ -224,8 +213,7 @@ namespace Bit.App.Pages
{ {
if (_inited && UriMatchSelectedIndex > -1) if (_inited && UriMatchSelectedIndex > -1)
{ {
await _storageService.SaveAsync(Constants.DefaultUriMatch, await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
(int?)UriMatchOptions[UriMatchSelectedIndex].Key);
} }
} }
@@ -233,7 +221,7 @@ namespace Bit.App.Pages
{ {
if (_inited) if (_inited)
{ {
await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt); await _stateService.SetAutofillDisableSavePromptAsync(AutofillDisableSavePrompt);
} }
} }
@@ -243,7 +231,7 @@ namespace Bit.App.Pages
{ {
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris)) if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
{ {
await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey); await _stateService.SetAutofillBlacklistedUrisAsync(null);
AutofillBlacklistedUris = null; AutofillBlacklistedUris = null;
return; return;
} }
@@ -265,7 +253,7 @@ namespace Bit.App.Pages
} }
urisList.Add(cleanedUri); urisList.Add(cleanedUri);
} }
await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList); await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
AutofillBlacklistedUris = string.Join(", ", urisList); AutofillBlacklistedUris = string.Join(", ", urisList);
} }
catch { } catch { }

View File

@@ -1,6 +1,5 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
@@ -17,12 +16,11 @@ namespace Bit.App.Pages
{ {
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IEnvironmentService _environmentService; private readonly IEnvironmentService _environmentService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IStorageService _storageService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IBiometricService _biometricService; private readonly IBiometricService _biometricService;
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
@@ -68,12 +66,11 @@ namespace Bit.App.Pages
{ {
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService"); _cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService"); _biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService"); _policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
@@ -111,7 +108,7 @@ namespace Bit.App.Pages
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout(); _vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key; _vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey) ?? "lock"; var action = await _stateService.GetVaultTimeoutActionAsync() ?? "lock";
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key; _vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync(); var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
_pin = pinSet.Item1 || pinSet.Item2; _pin = pinSet.Item1 || pinSet.Item2;
@@ -151,7 +148,7 @@ namespace Bit.App.Pages
List<string> fingerprint; List<string> fingerprint;
try try
{ {
fingerprint = await _cryptoService.GetFingerprintAsync(await _userService.GetUserIdAsync()); fingerprint = await _cryptoService.GetFingerprintAsync(await _stateService.GetActiveUserIdAsync());
} }
catch (Exception e) when (e.Message == "No public key available.") catch (Exception e) when (e.Message == "No public key available.")
{ {
@@ -329,9 +326,9 @@ namespace Bit.App.Pages
AppResources.Yes, AppResources.No); AppResources.Yes, AppResources.No);
} }
var kdf = await _userService.GetKdfAsync(); var kdf = await _stateService.GetKdfTypeAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync(); var kdfIterations = await _stateService.GetKdfIterationsAsync();
var email = await _userService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256), kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
kdfIterations.GetValueOrDefault(5000)); kdfIterations.GetValueOrDefault(5000));
@@ -341,12 +338,12 @@ namespace Bit.App.Pages
if (masterPassOnRestart) if (masterPassOnRestart)
{ {
var encPin = await _cryptoService.EncryptAsync(pin); var encPin = await _cryptoService.EncryptAsync(pin);
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString); await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
_vaultTimeoutService.PinProtectedKey = pinProtectedKey; await _stateService.SetPinProtectedCachedAsync(pinProtectedKey);
} }
else else
{ {
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString); await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
} }
} }
else else
@@ -381,13 +378,13 @@ namespace Bit.App.Pages
if (_biometric) if (_biometric)
{ {
await _biometricService.SetupBiometricAsync(); await _biometricService.SetupBiometricAsync();
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true); await _stateService.SetBiometricUnlockAsync(true);
} }
else else
{ {
await _storageService.RemoveAsync(Constants.BiometricUnlockKey); await _stateService.SetBiometricUnlockAsync(null);
} }
_vaultTimeoutService.BiometricLocked = false; _stateService.BiometricLocked = false;
await _cryptoService.ToggleKeyAsync(); await _cryptoService.ToggleKeyAsync();
BuildList(); BuildList();
} }

View File

@@ -1,6 +1,5 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -12,7 +11,7 @@ namespace Bit.App.Pages
{ {
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly ILocalizeService _localizeService; private readonly ILocalizeService _localizeService;
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
{ {
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService"); _localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
@@ -52,7 +51,7 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
await SetLastSyncAsync(); await SetLastSyncAsync();
EnableSyncOnRefresh = await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey); EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
_inited = true; _inited = true;
} }
@@ -60,7 +59,7 @@ namespace Bit.App.Pages
{ {
if (_inited) if (_inited)
{ {
await _storageService.SaveAsync(Constants.SyncOnRefreshKey, _syncOnRefresh); await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
} }
} }

View File

@@ -2,6 +2,7 @@
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
@@ -45,7 +46,7 @@ namespace Bit.App.Pages
var settingsPage = new NavigationPage(new SettingsPage(this)) var settingsPage = new NavigationPage(new SettingsPage(this))
{ {
Title = AppResources.Settings, Title = AppResources.Settings,
IconImageSource = "cog.png" IconImageSource = "cog_settings.png"
}; };
Children.Add(settingsPage); Children.Add(settingsPage);

View File

@@ -2,7 +2,6 @@
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
public partial class AddEditPage : BaseContentPage public partial class AddEditPage : BaseContentPage
{ {
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IKeyConnectorService _keyConnectorService; private readonly IKeyConnectorService _keyConnectorService;
@@ -38,7 +37,7 @@ namespace Bit.App.Pages
bool cloneMode = false, bool cloneMode = false,
ViewPage viewPage = null) ViewPage viewPage = null)
{ {
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"); _keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
@@ -333,10 +332,10 @@ namespace Bit.App.Pages
{ {
return; return;
} }
var addLoginShown = await _storageService.GetAsync<bool?>(Constants.AddSitePromptShownKey); var addLoginShown = await _stateService.GetAddSitePromptShownAsync();
if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault()) if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault())
{ {
await _storageService.SaveAsync(Constants.AddSitePromptShownKey, true); await _stateService.SetAddSitePromptShownAsync(true);
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
if (_deviceActionService.SystemMajorVersion() < 12) if (_deviceActionService.SystemMajorVersion() < 12)

View File

@@ -22,7 +22,8 @@ namespace Bit.App.Pages
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly IFolderService _folderService; private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService; private readonly ICollectionService _collectionService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IOrganizationService _organizationService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuditService _auditService; private readonly IAuditService _auditService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
@@ -70,7 +71,8 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService"); _cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_folderService = ServiceContainer.Resolve<IFolderService>("folderService"); _folderService = ServiceContainer.Resolve<IFolderService>("folderService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_auditService = ServiceContainer.Resolve<IAuditService>("auditService"); _auditService = ServiceContainer.Resolve<IAuditService>("auditService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
@@ -302,9 +304,9 @@ namespace Bit.App.Pages
public async Task<bool> LoadAsync(AppOptions appOptions = null) public async Task<bool> LoadAsync(AppOptions appOptions = null)
{ {
var myEmail = await _userService.GetEmailAsync(); var myEmail = await _stateService.GetEmailAsync();
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null)); OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
var orgs = await _userService.GetAllOrganizationAsync(); var orgs = await _organizationService.GetAllAsync();
foreach (var org in orgs.OrderBy(o => o.Name)) foreach (var org in orgs.OrderBy(o => o.Name))
{ {
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed) if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)

View File

@@ -17,8 +17,8 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IVaultTimeoutService _timeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private CipherView _cipher; private CipherView _cipher;
private Cipher _cipherDomain; private Cipher _cipherDomain;
@@ -33,8 +33,8 @@ namespace Bit.App.Pages
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService"); _cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService"); _cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_timeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
Attachments = new ExtendedObservableCollection<AttachmentView>(); Attachments = new ExtendedObservableCollection<AttachmentView>();
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync); DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
PageTitle = AppResources.Attachments; PageTitle = AppResources.Attachments;
@@ -66,7 +66,7 @@ namespace Bit.App.Pages
Cipher = await _cipherDomain.DecryptAsync(); Cipher = await _cipherDomain.DecryptAsync();
LoadAttachments(); LoadAttachments();
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync(); _hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
var canAccessPremium = await _userService.CanAccessPremiumAsync(); var canAccessPremium = await _stateService.CanAccessPremiumAsync();
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null; _canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
if (!_canAccessAttachments) if (!_canAccessAttachments)
{ {
@@ -140,7 +140,7 @@ namespace Bit.App.Pages
// Prevent Android from locking if vault timeout set to "immediate" // Prevent Android from locking if vault timeout set to "immediate"
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
_timeoutService.DelayLockAndLogoutMs = 60000; _vaultTimeoutService.DelayLockAndLogoutMs = 60000;
} }
await _deviceActionService.SelectFileAsync(); await _deviceActionService.SelectFileAsync();
} }

View File

@@ -8,10 +8,8 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
@@ -88,8 +86,7 @@ namespace Bit.App.Pages
public async Task LoadAsync() public async Task LoadAsync()
{ {
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey)) WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
.GetValueOrDefault();
ShowList = false; ShowList = false;
var groupedItems = new List<GroupingsPageListGroup>(); var groupedItems = new List<GroupingsPageListGroup>();
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null); var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);

View File

@@ -76,8 +76,7 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey)) WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
.GetValueOrDefault();
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text)) if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
{ {
Search((Page as CiphersPage).SearchBar.Text, 200); Search((Page as CiphersPage).SearchBar.Text, 200);

View File

@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms" <pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="Bit.App.Pages.GroupingsPage" x:Class="Bit.App.Pages.GroupingsPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:effects="clr-namespace:Bit.App.Effects" xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
x:DataType="pages:GroupingsPageViewModel" x:DataType="pages:GroupingsPageViewModel"
Title="{Binding PageTitle}" Title="{Binding PageTitle}"
x:Name="_page"> x:Name="_page">
@@ -15,6 +17,15 @@
</ContentPage.BindingContext> </ContentPage.BindingContext>
<ContentPage.ToolbarItems> <ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
<ToolbarItem Icon="search.png" Clicked="Search_Clicked" <ToolbarItem Icon="search.png" Clicked="Search_Clicked"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Search}" /> AutomationProperties.Name="{u:I18n Search}" />
@@ -135,6 +146,8 @@
AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"> AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView> </ContentView>
<!-- Android FAB -->
<Button <Button
x:Name="_fab" x:Name="_fab"
Image="plus.png" Image="plus.png"
@@ -148,6 +161,40 @@
<effects:FabShadowEffect /> <effects:FabShadowEffect />
</Button.Effects> </Button.Effects>
</Button> </Button>
<!-- Account Switching Overlay -->
<ContentView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
IsVisible="False"
BackgroundColor="#22000000"
Padding="0">
<StackLayout
x:Name="_accountListContainer"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}"
xct:ShadowEffect.Color="Black"
xct:ShadowEffect.Radius="10"
xct:ShadowEffect.OffsetY="3">
<ListView
x:Name="_accountListView"
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate x:DataType="view:AccountView">
<controls:AccountViewCell
Account="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView>
</AbsoluteLayout> </AbsoluteLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -1,7 +1,5 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -9,6 +7,7 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Controls; using Bit.App.Controls;
using Bit.Core.Models.Data;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
private readonly IBroadcasterService _broadcasterService; private readonly IBroadcasterService _broadcasterService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IPushNotificationService _pushNotificationService; private readonly IPushNotificationService _pushNotificationService;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
@@ -37,7 +36,7 @@ namespace Bit.App.Pages
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService"); _pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService"); _cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
@@ -70,6 +69,10 @@ namespace Bit.App.Pages
_absLayout.Children.Remove(_fab); _absLayout.Children.Remove(_fab);
ToolbarItems.Remove(_addItem); ToolbarItems.Remove(_addItem);
} }
if (!mainPage)
{
ToolbarItems.Remove(_accountAvatar);
}
} }
protected async override void OnAppearing() protected async override void OnAppearing()
@@ -80,6 +83,11 @@ namespace Bit.App.Pages
IsBusy = true; IsBusy = true;
} }
if (_vm.MainPage)
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
_broadcasterService.Subscribe(_pageName, async (message) => _broadcasterService.Subscribe(_pageName, async (message) =>
{ {
if (message.Command == "syncStarted") if (message.Command == "syncStarted")
@@ -100,7 +108,7 @@ namespace Bit.App.Pages
} }
}); });
var migratedFromV1 = await _storageService.GetAsync<bool?>(Constants.MigratedFromV1); var migratedFromV1 = await _stateService.GetMigratedFromV1Async();
await LoadOnAppearedAsync(_mainLayout, false, async () => await LoadOnAppearedAsync(_mainLayout, false, async () =>
{ {
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any()) if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
@@ -128,10 +136,10 @@ namespace Bit.App.Pages
!_vm.HasCiphers && !_vm.HasCiphers &&
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None) Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
{ {
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync); var triedV1ReSync = await _stateService.GetTriedV1ResyncAsync();
if (!triedV1ReSync.GetValueOrDefault()) if (!triedV1ReSync.GetValueOrDefault())
{ {
await _storageService.SaveAsync(Constants.TriedV1Resync, true); await _stateService.SetTriedV1ResyncAsync(true);
await _syncService.FullSyncAsync(true); await _syncService.FullSyncAsync(true);
} }
} }
@@ -145,14 +153,14 @@ namespace Bit.App.Pages
} }
// Push registration // Push registration
var lastPushRegistration = await _storageService.GetAsync<DateTime?>(Constants.PushLastRegistrationDateKey); var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue); lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
var pushPromptShow = await _storageService.GetAsync<bool?>(Constants.PushInitialPromptShownKey); var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
if (!pushPromptShow.GetValueOrDefault(false)) if (!pushPromptShow.GetValueOrDefault(false))
{ {
await _storageService.SaveAsync(Constants.PushInitialPromptShownKey, true); await _stateService.SetPushInitialPromptShownAsync(true);
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert, await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
AppResources.OkGotIt); AppResources.OkGotIt);
} }
@@ -173,8 +181,8 @@ namespace Bit.App.Pages
{ {
if (migratedFromV1.GetValueOrDefault()) if (migratedFromV1.GetValueOrDefault())
{ {
var migratedFromV1AutofillPromptShown = await _storageService.GetAsync<bool?>( var migratedFromV1AutofillPromptShown =
Constants.MigratedFromV1AutofillPromptShown); await _stateService.GetMigratedFromV1AutofillPromptShownAsync();
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault()) if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
{ {
await DisplayAlert(AppResources.Autofill, await DisplayAlert(AppResources.Autofill,
@@ -182,7 +190,7 @@ namespace Bit.App.Pages
} }
} }
} }
await _storageService.SaveAsync(Constants.MigratedFromV1AutofillPromptShown, true); await _stateService.SetMigratedFromV1AutofillPromptShownAsync(true);
} }
} }
@@ -284,5 +292,24 @@ namespace Bit.App.Pages
_addItem.IsEnabled = !_vm.Deleted; _addItem.IsEnabled = !_vm.Deleted;
_addItem.IconImageSource = _vm.Deleted ? null : "plus.png"; _addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
} }
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay, _fab);
}
else
{
await RefreshAccountViewsAsync(_accountListView, true);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay, _fab);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, _fab);
}
} }
} }

View File

@@ -1,7 +1,6 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
@@ -11,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -39,13 +39,11 @@ namespace Bit.App.Pages
private readonly IFolderService _folderService; private readonly IFolderService _folderService;
private readonly ICollectionService _collectionService; private readonly ICollectionService _collectionService;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IUserService _userService;
private readonly IVaultTimeoutService _vaultTimeoutService; private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IStorageService _storageService;
private readonly IPasswordRepromptService _passwordRepromptService; private readonly IPasswordRepromptService _passwordRepromptService;
public GroupingsPageViewModel() public GroupingsPageViewModel()
@@ -54,13 +52,11 @@ namespace Bit.App.Pages
_folderService = ServiceContainer.Resolve<IFolderService>("folderService"); _folderService = ServiceContainer.Resolve<IFolderService>("folderService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService"); _collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"); _passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
Loading = true; Loading = true;
@@ -80,7 +76,6 @@ namespace Bit.App.Pages
public string CollectionId { get; set; } public string CollectionId { get; set; }
public Func<CipherView, bool> Filter { get; set; } public Func<CipherView, bool> Filter { get; set; }
public bool Deleted { get; set; } public bool Deleted { get; set; }
public bool HasCiphers { get; set; } public bool HasCiphers { get; set; }
public bool HasFolders { get; set; } public bool HasFolders { get; set; }
public bool HasCollections { get; set; } public bool HasCollections { get; set; }
@@ -139,6 +134,11 @@ namespace Bit.App.Pages
get => _websiteIconsEnabled; get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value); set => SetProperty(ref _websiteIconsEnabled, value);
} }
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; } public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
public Command RefreshCommand { get; set; } public Command RefreshCommand { get; set; }
public Command<CipherView> CipherOptionsCommand { get; set; } public Command<CipherView> CipherOptionsCommand { get; set; }
@@ -150,7 +150,7 @@ namespace Bit.App.Pages
{ {
return; return;
} }
var authed = await _userService.IsAuthenticatedAsync(); var authed = await _stateService.IsAuthenticatedAsync();
if (!authed) if (!authed)
{ {
return; return;
@@ -159,7 +159,7 @@ namespace Bit.App.Pages
{ {
return; return;
} }
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing) if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{ {
SyncRefreshing = true; SyncRefreshing = true;
await _syncService.FullSyncAsync(false); await _syncService.FullSyncAsync(false);
@@ -175,8 +175,7 @@ namespace Bit.App.Pages
var groupedItems = new List<GroupingsPageListGroup>(); var groupedItems = new List<GroupingsPageListGroup>();
var page = Page as GroupingsPage; var page = Page as GroupingsPage;
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey)) WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
.GetValueOrDefault();
try try
{ {
await LoadDataAsync(); await LoadDataAsync();

View File

@@ -16,7 +16,7 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly ICollectionService _collectionService; private readonly ICollectionService _collectionService;
private readonly IUserService _userService; private readonly IOrganizationService _organizationService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private CipherView _cipher; private CipherView _cipher;
private int _organizationSelectedIndex; private int _organizationSelectedIndex;
@@ -28,7 +28,7 @@ namespace Bit.App.Pages
{ {
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService"); _cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService"); _collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
Collections = new ExtendedObservableCollection<CollectionViewModel>(); Collections = new ExtendedObservableCollection<CollectionViewModel>();
@@ -67,7 +67,7 @@ namespace Bit.App.Pages
var allCollections = await _collectionService.GetAllDecryptedAsync(); var allCollections = await _collectionService.GetAllDecryptedAsync();
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList(); _writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
var orgs = await _userService.GetAllOrganizationAsync(); var orgs = await _organizationService.GetAllAsync();
OrganizationOptions = orgs.OrderBy(o => o.Name) OrganizationOptions = orgs.OrderBy(o => o.Name)
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed) .Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
.Select(o => new KeyValuePair<string, string>(o.Name, o.Id)).ToList(); .Select(o => new KeyValuePair<string, string>(o.Name, o.Id)).ToList();
@@ -110,7 +110,7 @@ namespace Bit.App.Pages
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds); await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name, var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
(await _userService.GetOrganizationAsync(OrganizationId)).Name); (await _organizationService.GetAsync(OrganizationId)).Name);
_platformUtilsService.ShowToast("success", null, movedItemToOrgText); _platformUtilsService.ShowToast("success", null, movedItemToOrgText);
await Page.Navigation.PopModalAsync(); await Page.Navigation.PopModalAsync();
return true; return true;

View File

@@ -18,7 +18,7 @@ namespace Bit.App.Pages
{ {
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly ITotpService _totpService; private readonly ITotpService _totpService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuditService _auditService; private readonly IAuditService _auditService;
@@ -48,7 +48,7 @@ namespace Bit.App.Pages
{ {
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); _deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService"); _cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_totpService = ServiceContainer.Resolve<ITotpService>("totpService"); _totpService = ServiceContainer.Resolve<ITotpService>("totpService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_auditService = ServiceContainer.Resolve<IAuditService>("auditService"); _auditService = ServiceContainer.Resolve<IAuditService>("auditService");
@@ -248,7 +248,7 @@ namespace Bit.App.Pages
return false; return false;
} }
Cipher = await cipher.DecryptAsync(); Cipher = await cipher.DecryptAsync();
CanAccessPremium = await _userService.CanAccessPremiumAsync(); CanAccessPremium = await _stateService.CanAccessPremiumAsync();
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList(); Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) && if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&

View File

@@ -13,15 +13,10 @@ namespace Bit.App.Services
private readonly HashSet<string> _preferenceStorageKeys = new HashSet<string> private readonly HashSet<string> _preferenceStorageKeys = new HashSet<string>
{ {
Constants.VaultTimeoutKey, Constants.AppIdKey,
Constants.VaultTimeoutActionKey, Constants.PreAuthEnvironmentUrlsKey,
Constants.ThemeKey, Constants.AutofillTileAdded,
Constants.DefaultUriMatch, Constants.AddSitePromptShownKey,
Constants.DisableAutoTotpCopyKey,
Constants.DisableFaviconKey,
Constants.ClearClipboardKey,
Constants.AutofillDisableSavePromptKey,
Constants.LastActiveTimeKey,
Constants.PushInitialPromptShownKey, Constants.PushInitialPromptShownKey,
Constants.LastFileCacheClearKey, Constants.LastFileCacheClearKey,
Constants.PushLastRegistrationDateKey, Constants.PushLastRegistrationDateKey,
@@ -37,14 +32,17 @@ namespace Bit.App.Services
Constants.iOSAutoFillBiometricIntegrityKey, Constants.iOSAutoFillBiometricIntegrityKey,
Constants.iOSExtensionClearCiphersCacheKey, Constants.iOSExtensionClearCiphersCacheKey,
Constants.iOSExtensionBiometricIntegrityKey, Constants.iOSExtensionBiometricIntegrityKey,
Constants.EnvironmentUrlsKey, Constants.RememberEmailKey,
Constants.InlineAutofillEnabledKey, Constants.RememberedEmailKey,
Constants.InvalidUnlockAttempts, Constants.RememberOrgIdentifierKey,
Constants.RememberedOrgIdentifierKey,
Constants.AppExtensionStartedKey,
Constants.AppExtensionActivatedKey,
}; };
private readonly HashSet<string> _migrateToPreferences = new HashSet<string> private readonly HashSet<string> _migrateToPreferences = new HashSet<string>
{ {
Constants.EnvironmentUrlsKey, "environmentUrls",
}; };
private readonly HashSet<string> _haveMigratedToPreferences = new HashSet<string>(); private readonly HashSet<string> _haveMigratedToPreferences = new HashSet<string>();

View File

@@ -19,9 +19,8 @@ namespace Bit.App.Services
{ {
private bool _showNotification; private bool _showNotification;
private bool _resolved; private bool _resolved;
private IStorageService _storageService;
private ISyncService _syncService; private ISyncService _syncService;
private IUserService _userService; private IStateService _stateService;
private IAppIdService _appIdService; private IAppIdService _appIdService;
private IApiService _apiService; private IApiService _apiService;
private IMessagingService _messagingService; private IMessagingService _messagingService;
@@ -58,8 +57,8 @@ namespace Bit.App.Services
return; return;
} }
var myUserId = await _userService.GetUserIdAsync(); var myUserId = await _stateService.GetActiveUserIdAsync();
var isAuthenticated = await _userService.IsAuthenticatedAsync(); var isAuthenticated = await _stateService.IsAuthenticatedAsync();
switch (notification.Type) switch (notification.Type)
{ {
case NotificationType.SyncCipherUpdate: case NotificationType.SyncCipherUpdate:
@@ -129,7 +128,7 @@ namespace Bit.App.Services
{ {
Resolve(); Resolve();
Debug.WriteLine(string.Format("Push Notification - Device Registered - Token : {0}", token)); Debug.WriteLine(string.Format("Push Notification - Device Registered - Token : {0}", token));
var isAuthenticated = await _userService.IsAuthenticatedAsync(); var isAuthenticated = await _stateService.IsAuthenticatedAsync();
if (!isAuthenticated) if (!isAuthenticated)
{ {
return; return;
@@ -141,10 +140,10 @@ namespace Bit.App.Services
await _apiService.PutDeviceTokenAsync(appId, await _apiService.PutDeviceTokenAsync(appId,
new Core.Models.Request.DeviceTokenRequest { PushToken = token }); new Core.Models.Request.DeviceTokenRequest { PushToken = token });
Debug.WriteLine("Registered device with server."); Debug.WriteLine("Registered device with server.");
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow); await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
if (deviceType == Device.Android) if (deviceType == Device.Android)
{ {
await _storageService.SaveAsync(Constants.PushCurrentTokenKey, token); await _stateService.SetPushCurrentTokenAsync(token);
} }
} }
catch (ApiException) catch (ApiException)
@@ -174,9 +173,8 @@ namespace Bit.App.Services
{ {
return; return;
} }
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_userService = ServiceContainer.Resolve<IUserService>("userService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService"); _appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService"); _apiService = ServiceContainer.Resolve<IApiService>("apiService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");

View File

@@ -2,7 +2,6 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Pages; using Bit.App.Pages;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -14,6 +13,7 @@ using System.Threading.Tasks;
using Bit.App.Models; using Bit.App.Models;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Newtonsoft.Json; using Newtonsoft.Json;
using Xamarin.Essentials; using Xamarin.Essentials;
using Xamarin.Forms; using Xamarin.Forms;
@@ -46,8 +46,8 @@ namespace Bit.App.Utilities
} }
if (!string.IsNullOrWhiteSpace(cipher.Login.Totp)) if (!string.IsNullOrWhiteSpace(cipher.Login.Totp))
{ {
var userService = ServiceContainer.Resolve<IUserService>("userService"); var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var canAccessPremium = await userService.CanAccessPremiumAsync(); var canAccessPremium = await stateService.CanAccessPremiumAsync();
if (canAccessPremium || cipher.OrganizationUseTotp) if (canAccessPremium || cipher.OrganizationUseTotp)
{ {
options.Add(AppResources.CopyTotp); options.Add(AppResources.CopyTotp);
@@ -330,33 +330,15 @@ namespace Bit.App.Utilities
} }
public static async Task<bool> PerformUpdateTasksAsync(ISyncService syncService, public static async Task<bool> PerformUpdateTasksAsync(ISyncService syncService,
IDeviceActionService deviceActionService, IStorageService storageService) IDeviceActionService deviceActionService, IStateService stateService)
{ {
var currentBuild = deviceActionService.GetBuildNumber(); var currentBuild = deviceActionService.GetBuildNumber();
var lastBuild = await storageService.GetAsync<string>(Constants.LastBuildKey); var lastBuild = await stateService.GetLastBuildAsync();
if (lastBuild == null) if (lastBuild == null || lastBuild != currentBuild)
{
// Installed
var currentTimeout = await storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
if (currentTimeout == null)
{
await storageService.SaveAsync(Constants.VaultTimeoutKey, 15);
}
var currentAction = await storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
if (currentAction == null)
{
await storageService.SaveAsync(Constants.VaultTimeoutActionKey, "lock");
}
}
else if (lastBuild != currentBuild)
{ {
// Updated // Updated
var tasks = Task.Run(() => syncService.FullSyncAsync(true)); var tasks = Task.Run(() => syncService.FullSyncAsync(true));
} await stateService.SetLastBuildAsync(currentBuild);
if (lastBuild != currentBuild)
{
await storageService.SaveAsync(Constants.LastBuildKey, currentBuild);
return true; return true;
} }
return false; return false;
@@ -418,35 +400,34 @@ namespace Bit.App.Utilities
public static async Task<PreviousPageInfo> ClearPreviousPage() public static async Task<PreviousPageInfo> ClearPreviousPage()
{ {
var storageService = ServiceContainer.Resolve<IStorageService>("storageService"); var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var previousPage = await storageService.GetAsync<PreviousPageInfo>(Constants.PreviousPageKey); var previousPage = await stateService.GetPreviousPageInfoAsync();
if (previousPage != null) if (previousPage != null)
{ {
await storageService.RemoveAsync(Constants.PreviousPageKey); await stateService.SetPreviousPageInfoAsync(null);
} }
return previousPage; return previousPage;
} }
public static async Task<int> IncrementInvalidUnlockAttemptsAsync() public static async Task<int> IncrementInvalidUnlockAttemptsAsync()
{ {
var storageService = ServiceContainer.Resolve<IStorageService>("storageService"); var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var invalidUnlockAttempts = await storageService.GetAsync<int>(Constants.InvalidUnlockAttempts); var invalidUnlockAttempts = await stateService.GetInvalidUnlockAttemptsAsync();
invalidUnlockAttempts++; invalidUnlockAttempts++;
await storageService.SaveAsync(Constants.InvalidUnlockAttempts, invalidUnlockAttempts); await stateService.SetInvalidUnlockAttemptsAsync(invalidUnlockAttempts);
return invalidUnlockAttempts; return invalidUnlockAttempts;
} }
public static async Task ResetInvalidUnlockAttemptsAsync() public static async Task ResetInvalidUnlockAttemptsAsync()
{ {
var storageService = ServiceContainer.Resolve<IStorageService>("storageService"); var stateService = ServiceContainer.Resolve<IStateService>("stateService");
await storageService.RemoveAsync(Constants.InvalidUnlockAttempts); await stateService.SetInvalidUnlockAttemptsAsync(null);
} }
public static async Task<bool> IsVaultTimeoutImmediateAsync() public static async Task<bool> IsVaultTimeoutImmediateAsync()
{ {
var storageService = ServiceContainer.Resolve<IStorageService>("storageService"); var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var vaultTimeoutMinutes = await stateService.GetVaultTimeoutAsync();
var vaultTimeoutMinutes = await storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
if (vaultTimeoutMinutes.GetValueOrDefault(-1) == 0) if (vaultTimeoutMinutes.GetValueOrDefault(-1) == 0)
{ {
return true; return true;
@@ -466,9 +447,8 @@ namespace Bit.App.Utilities
return Convert.ToBase64String(Encoding.UTF8.GetBytes(multiByteEscaped)); return Convert.ToBase64String(Encoding.UTF8.GetBytes(multiByteEscaped));
} }
public static async Task LogOutAsync() public static async Task LogOutAsync(string userId, bool userInitiated = false)
{ {
var userService = ServiceContainer.Resolve<IUserService>("userService");
var syncService = ServiceContainer.Resolve<ISyncService>("syncService"); var syncService = ServiceContainer.Resolve<ISyncService>("syncService");
var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService"); var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService"); var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
@@ -483,21 +463,61 @@ namespace Bit.App.Utilities
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"); var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
var searchService = ServiceContainer.Resolve<ISearchService>("searchService"); var searchService = ServiceContainer.Resolve<ISearchService>("searchService");
var userId = await userService.GetUserIdAsync(); if (userId == null)
{
userId = await stateService.GetActiveUserIdAsync();
}
await Task.WhenAll( await Task.WhenAll(
syncService.SetLastSyncAsync(DateTime.MinValue),
tokenService.ClearTokenAsync(),
cryptoService.ClearKeysAsync(),
userService.ClearAsync(),
settingsService.ClearAsync(userId),
cipherService.ClearAsync(userId), cipherService.ClearAsync(userId),
folderService.ClearAsync(userId), folderService.ClearAsync(userId),
collectionService.ClearAsync(userId), collectionService.ClearAsync(userId),
passwordGenerationService.ClearAsync(), passwordGenerationService.ClearAsync(userId),
vaultTimeoutService.ClearAsync(),
stateService.PurgeAsync(),
deviceActionService.ClearCacheAsync()); deviceActionService.ClearCacheAsync());
vaultTimeoutService.BiometricLocked = true; if (userInitiated)
{
await Task.WhenAll(
syncService.SetLastSyncAsync(DateTime.MinValue),
tokenService.ClearTokenAsync(userId),
cryptoService.ClearKeysAsync(userId),
settingsService.ClearAsync(userId),
vaultTimeoutService.ClearAsync(userId),
stateService.ClearAsync(userId));
if (await stateService.GetActiveUserIdAsync() != null)
{
var messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
messagingService.Send("switchedAccount");
}
}
stateService.BiometricLocked = true;
searchService.ClearIndex();
}
public static async Task OnAccountSwitchAsync()
{
var environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
var settingsService = ServiceContainer.Resolve<ISettingsService>("settingsService");
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
var folderService = ServiceContainer.Resolve<IFolderService>("folderService");
var collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
var passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
"passwordGenerationService");
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
var searchService = ServiceContainer.Resolve<ISearchService>("searchService");
await environmentService.SetUrlsFromStorageAsync();
await Task.WhenAll(
cipherService.ClearCacheAsync(),
deviceActionService.ClearCacheAsync());
tokenService.ClearCache();
cryptoService.ClearCache();
settingsService.ClearCache();
folderService.ClearCache();
collectionService.ClearCache();
passwordGenerationService.ClearCache();
searchService.ClearIndex(); searchService.ClearIndex();
} }
} }

View File

@@ -1,8 +1,8 @@
using System; using System;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Services;
using Bit.App.Styles; using Bit.App.Styles;
using Bit.Core; using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
#if !FDROID #if !FDROID
using Microsoft.AppCenter.Crashes; using Microsoft.AppCenter.Crashes;
@@ -93,16 +93,15 @@ namespace Bit.App.Utilities
} }
} }
public static void SetTheme(bool android, ResourceDictionary resources) public static void SetTheme(ResourceDictionary resources)
{ {
SetThemeStyle(GetTheme(android), resources); SetThemeStyle(GetTheme(), resources);
} }
public static string GetTheme(bool android) public static string GetTheme()
{ {
return Xamarin.Essentials.Preferences.Get( var stateService = ServiceContainer.Resolve<IStateService>("stateService");
string.Format(PreferencesStorageService.KeyFormat, Constants.ThemeKey), default(string), return stateService.GetThemeAsync().GetAwaiter().GetResult();
!android ? "group.com.8bit.bitwarden" : default(string));
} }
public static bool OsDarkModeEnabled() public static bool OsDarkModeEnabled()

View File

@@ -5,6 +5,5 @@ namespace Bit.Core.Abstractions
public interface IAppIdService public interface IAppIdService
{ {
Task<string> GetAppIdAsync(); Task<string> GetAppIdAsync();
Task<string> GetAnonymousAppIdAsync();
} }
} }

View File

@@ -22,4 +22,4 @@ namespace Bit.Core.Abstractions
Task UpsertAsync(CollectionData collection); Task UpsertAsync(CollectionData collection);
Task UpsertAsync(List<CollectionData> collection); Task UpsertAsync(List<CollectionData> collection);
} }
} }

View File

@@ -9,13 +9,14 @@ namespace Bit.Core.Abstractions
{ {
public interface ICryptoService public interface ICryptoService
{ {
Task ClearEncKeyAsync(bool memoryOnly = false); Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
Task ClearKeyAsync(); Task ClearKeyAsync(string userId = null);
Task ClearKeyHashAsync(); Task ClearKeyHashAsync(string userId = null);
Task ClearKeyPairAsync(bool memoryOnly = false); Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
Task ClearKeysAsync(); Task ClearKeysAsync(string userId = null);
Task ClearOrgKeysAsync(bool memoryOnly = false); Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
Task ClearPinProtectedKeyAsync(); Task ClearPinProtectedKeyAsync(string userId = null);
void ClearCache();
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key); Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null); Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null);
Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null); Task<string> DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null);
@@ -24,7 +25,7 @@ namespace Bit.Core.Abstractions
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null); Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null); Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null); Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
Task<SymmetricCryptoKey> GetKeyAsync(); Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
Task<string> GetKeyHashAsync(); Task<string> GetKeyHashAsync();
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId); Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync(); Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
@@ -33,7 +34,7 @@ namespace Bit.Core.Abstractions
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key); Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key);
Task<bool> HasEncKeyAsync(); Task<bool> HasEncKeyAsync();
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); 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<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations); Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations);
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations, Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations,

View File

@@ -9,4 +9,4 @@ namespace Bit.Core.Abstractions
Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false); Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false);
Task UploadEventsAsync(); Task UploadEventsAsync();
} }
} }

View File

@@ -23,4 +23,4 @@ namespace Bit.Core.Abstractions
Task UpsertAsync(FolderData folder); Task UpsertAsync(FolderData folder);
Task UpsertAsync(List<FolderData> folder); Task UpsertAsync(List<FolderData> folder);
} }
} }

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
namespace Bit.Core.Abstractions
{
public interface IOrganizationService
{
Task<Organization> GetAsync(string id);
Task<Organization> GetByIdentifierAsync(string identifier);
Task<List<Organization>> GetAllAsync(string userId = null);
Task ReplaceAsync(Dictionary<string, OrganizationData> organizations);
Task ClearAllAsync(string userId);
}
}

View File

@@ -8,7 +8,8 @@ namespace Bit.Core.Abstractions
public interface IPasswordGenerationService public interface IPasswordGenerationService
{ {
Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken)); Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken));
Task ClearAsync(); Task ClearAsync(string userId = null);
void ClearCache();
Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options); Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options);
Task<string> GeneratePasswordAsync(PasswordGenerationOptions options); Task<string> GeneratePasswordAsync(PasswordGenerationOptions options);
Task<List<GeneratedPasswordHistory>> GetHistoryAsync(); Task<List<GeneratedPasswordHistory>> GetHistoryAsync();

View File

@@ -10,4 +10,4 @@ namespace Bit.Core.Abstractions
Task<List<List<string>>> GetEquivalentDomainsAsync(); Task<List<List<string>>> GetEquivalentDomainsAsync();
Task SetEquivalentDomainsAsync(List<List<string>> equivalentDomains); Task SetEquivalentDomainsAsync(List<List<string>> equivalentDomains);
} }
} }

View File

@@ -1,12 +1,163 @@
using System.Threading.Tasks; using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.Core.Abstractions namespace Bit.Core.Abstractions
{ {
public interface IStateService public interface IStateService
{ {
Task<T> GetAsync<T>(string key); bool BiometricLocked { get; set; }
Task RemoveAsync(string key); ExtendedObservableCollection<AccountView> AccountViews { get; set; }
Task SaveAsync<T>(string key, T obj); Task<List<string>> GetUserIdsAsync();
Task PurgeAsync(); Task<string> GetActiveUserIdAsync();
Task SetActiveUserAsync(string userId);
Task<bool> IsAuthenticatedAsync(string userId = null);
Task<bool> HasMultipleAccountsAsync();
Task RefreshAccountViewsAsync(bool allowAddAccountRow);
Task AddAccountAsync(Account account);
Task ClearAsync(string userId);
Task<EnvironmentUrlData> GetPreAuthEnvironmentUrlsAsync();
Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value);
Task<EnvironmentUrlData> GetEnvironmentUrlsAsync(string userId = null);
Task<bool?> GetBiometricUnlockAsync(string userId = null);
Task SetBiometricUnlockAsync(bool? value, string userId = null);
Task<bool> CanAccessPremiumAsync(string userId = null);
Task<string> GetProtectedPinAsync(string userId = null);
Task SetProtectedPinAsync(string value, string userId = null);
Task<string> GetPinProtectedAsync(string userId = null);
Task SetPinProtectedAsync(string value, string userId = null);
Task<EncString> GetPinProtectedCachedAsync(string userId = null);
Task SetPinProtectedCachedAsync(EncString value, string userId = null);
Task<KdfType?> GetKdfTypeAsync(string userId = null);
Task SetKdfTypeAsync(KdfType? value, string userId = null);
Task<int?> GetKdfIterationsAsync(string userId = null);
Task SetKdfIterationsAsync(int? value, string userId = null);
Task<string> GetKeyEncryptedAsync(string userId = null);
Task SetKeyEncryptedAsync(string value, string userId = null);
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null);
Task<string> GetKeyHashAsync(string userId = null);
Task SetKeyHashAsync(string value, string userId = null);
Task<string> GetEncKeyEncryptedAsync(string userId = null);
Task SetEncKeyEncryptedAsync(string value, string userId = null);
Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null);
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
Task SetPrivateKeyEncryptedAsync(string value, string userId = null);
Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null);
Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null);
Task<bool?> GetAutofillTileAddedAsync(string userId = null);
Task SetAutofillTileAddedAsync(bool? value, string userId = null);
Task<string> GetEmailAsync(string userId = null);
// Task SetEmailAsync(string value, string userId = null);
Task<long?> GetLastActiveTimeAsync(string userId = null);
Task SetLastActiveTimeAsync(long? value, string userId = null);
Task<int?> GetVaultTimeoutAsync(string userId = null);
Task SetVaultTimeoutAsync(int? value, string userId = null);
Task<string> GetVaultTimeoutActionAsync(string userId = null);
Task SetVaultTimeoutActionAsync(string value, string userId = null);
Task<DateTime?> GetLastFileCacheClearAsync(string userId = null);
Task SetLastFileCacheClearAsync(DateTime? value, string userId = null);
Task<PreviousPageInfo> GetPreviousPageInfoAsync(string userId = null);
Task SetPreviousPageInfoAsync(PreviousPageInfo value, string userId = null);
Task<int> GetInvalidUnlockAttemptsAsync(string userId = null);
Task SetInvalidUnlockAttemptsAsync(int? value, string userId = null);
Task<string> GetLastBuildAsync(string userId = null);
Task SetLastBuildAsync(string value, string userId = null);
Task<bool?> GetDisableFaviconAsync(string userId = null);
Task SetDisableFaviconAsync(bool? value, string userId = null);
Task<bool?> GetDisableAutoTotpCopyAsync(string userId = null);
Task SetDisableAutoTotpCopyAsync(bool? value, string userId = null);
Task<bool?> GetInlineAutofillEnabledAsync(string userId = null);
Task SetInlineAutofillEnabledAsync(bool? value, string userId = null);
Task<bool?> GetAutofillDisableSavePromptAsync(string userId = null);
Task SetAutofillDisableSavePromptAsync(bool? value, string userId = null);
Task<Dictionary<string, Dictionary<string, object>>> GetLocalDataAsync(string userId = null);
Task SetLocalDataAsync(Dictionary<string, Dictionary<string, object>> value, string userId = null);
Task<Dictionary<string, CipherData>> GetEncryptedCiphersAsync(string userId = null);
Task SetEncryptedCiphersAsync(Dictionary<string, CipherData> value, string userId = null);
Task<int?> GetDefaultUriMatchAsync(string userId = null);
Task SetDefaultUriMatchAsync(int? value, string userId = null);
Task<HashSet<string>> GetNeverDomainsAsync(string userId = null);
Task SetNeverDomainsAsync(HashSet<string> value, string userId = null);
Task<int?> GetClearClipboardAsync(string userId = null);
Task SetClearClipboardAsync(int? value, string userId = null);
Task<Dictionary<string, CollectionData>> GetEncryptedCollectionsAsync(string userId = null);
Task SetEncryptedCollectionsAsync(Dictionary<string, CollectionData> value, string userId = null);
Task<bool> GetPasswordRepromptAutofillAsync(string userId = null);
Task SetPasswordRepromptAutofillAsync(bool? value, string userId = null);
Task<bool> GetPasswordVerifiedAutofillAsync(string userId = null);
Task SetPasswordVerifiedAutofillAsync(bool? value, string userId = null);
Task<DateTime?> GetLastSyncAsync(string userId = null);
Task SetLastSyncAsync(DateTime? value, string userId = null);
Task<string> GetSecurityStampAsync(string userId = null);
Task SetSecurityStampAsync(string value, string userId = null);
Task<bool> GetEmailVerifiedAsync(string userId = null);
Task SetEmailVerifiedAsync(bool? value, string userId = null);
Task<bool> GetForcePasswordReset(string userId = null);
Task SetForcePasswordResetAsync(bool? value, string userId = null);
Task<bool> GetSyncOnRefreshAsync(string userId = null);
Task SetSyncOnRefreshAsync(bool? value, string userId = null);
Task<string> GetRememberedEmailAsync(string userId = null);
Task SetRememberedEmailAsync(string value, string userId = null);
Task<bool?> GetRememberEmailAsync(string userId = null);
Task SetRememberEmailAsync(bool? value, string userId = null);
Task<string> GetRememberedOrgIdentifierAsync(string userId = null);
Task SetRememberedOrgIdentifierAsync(string value, string userId = null);
Task<bool?> GetRememberOrgIdentifierAsync(string userId = null);
Task SetRememberOrgIdentifierAsync(bool? value, string userId = null);
Task<string> GetThemeAsync(string userId = null);
Task SetThemeAsync(string value, string userId = null);
Task<bool?> GetAddSitePromptShownAsync(string userId = null);
Task SetAddSitePromptShownAsync(bool? value, string userId = null);
Task<bool?> GetMigratedFromV1Async(string userId = null);
Task SetMigratedFromV1Async(bool? value, string userId = null);
Task<bool?> GetMigratedFromV1AutofillPromptShownAsync(string userId = null);
Task SetMigratedFromV1AutofillPromptShownAsync(bool? value, string userId = null);
Task<bool?> GetTriedV1ResyncAsync(string userId = null);
Task SetTriedV1ResyncAsync(bool? value, string userId = null);
Task<bool?> GetPushInitialPromptShownAsync(string userId = null);
Task SetPushInitialPromptShownAsync(bool? value, string userId = null);
Task<DateTime?> GetPushLastRegistrationDateAsync(string userId = null);
Task SetPushLastRegistrationDateAsync(DateTime? value, string userId = null);
Task<string> GetPushCurrentTokenAsync(string userId = null);
Task SetPushCurrentTokenAsync(string value, string userId = null);
Task<List<EventData>> GetEventCollectionAsync(string userId = null);
Task SetEventCollectionAsync(List<EventData> value, string userId = null);
Task<Dictionary<string, FolderData>> GetEncryptedFoldersAsync(string userId = null);
Task SetEncryptedFoldersAsync(Dictionary<string, FolderData> value, string userId = null);
Task<Dictionary<string, PolicyData>> GetEncryptedPoliciesAsync(string userId = null);
Task SetEncryptedPoliciesAsync(Dictionary<string, PolicyData> value, string userId = null);
Task<string> GetPushRegisteredTokenAsync();
Task SetPushRegisteredTokenAsync(string value);
Task<bool?> GetAppExtensionStartedAsync(string userId = null);
Task SetAppExtensionStartedAsync(bool? value, string userId = null);
Task<bool?> GetAppExtensionActivatedAsync(string userId = null);
Task SetAppExtensionActivatedAsync(bool? value, string userId = null);
Task<string> GetAppIdAsync(string userId = null);
Task SetAppIdAsync(string value, string userId = null);
Task<bool> GetUsesKeyConnectorAsync(string userId = null);
Task SetUsesKeyConnectorAsync(bool? value, string userId = null);
Task<Dictionary<string, OrganizationData>> GetOrganizationsAsync(string userId = null);
Task SetOrganizationsAsync(Dictionary<string, OrganizationData> organizations, string userId = null);
Task<PasswordGenerationOptions> GetPasswordGenerationOptionsAsync(string userId = null);
Task SetPasswordGenerationOptionsAsync(PasswordGenerationOptions value, string userId = null);
Task<List<GeneratedPasswordHistory>> GetEncryptedPasswordGenerationHistory(string userId = null);
Task SetEncryptedPasswordGenerationHistoryAsync(List<GeneratedPasswordHistory> value, string userId = null);
Task<Dictionary<string, SendData>> GetEncryptedSendsAsync(string userId = null);
Task SetEncryptedSendsAsync(Dictionary<string, SendData> value, string userId = null);
Task<Dictionary<string, object>> GetSettingsAsync(string userId = null);
Task SetSettingsAsync(Dictionary<string, object> value, string userId = null);
Task<string> GetAccessTokenAsync(string userId = null);
Task SetAccessTokenAsync(string value, bool skipTokenStorage, string userId = null);
Task<string> GetRefreshTokenAsync(string userId = null);
Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null);
Task<string> GetTwoFactorTokenAsync(string email = null);
Task SetTwoFactorTokenAsync(string value, string email = null);
} }
} }

View File

@@ -6,15 +6,16 @@ namespace Bit.Core.Abstractions
{ {
public interface ITokenService public interface ITokenService
{ {
Task ClearTokenAsync(); Task ClearTokenAsync(string userId = null);
Task ClearTwoFactorTokenAsync(string email); Task ClearTwoFactorTokenAsync(string email);
void ClearCache();
JObject DecodeToken(); JObject DecodeToken();
string GetEmail(); string GetEmail();
bool GetEmailVerified(); bool GetEmailVerified();
string GetIssuer(); string GetIssuer();
string GetName(); string GetName();
bool GetPremium(); bool GetPremium();
bool GetIsExternal(); Task<bool> GetIsExternal();
Task<string> GetRefreshTokenAsync(); Task<string> GetRefreshTokenAsync();
Task<string> GetTokenAsync(); Task<string> GetTokenAsync();
Task ToggleTokensAsync(); Task ToggleTokensAsync();
@@ -22,7 +23,7 @@ namespace Bit.Core.Abstractions
Task<string> GetTwoFactorTokenAsync(string email); Task<string> GetTwoFactorTokenAsync(string email);
string GetUserId(); string GetUserId();
Task SetRefreshTokenAsync(string refreshToken); Task SetRefreshTokenAsync(string refreshToken);
Task SetTokenAsync(string token); Task SetAccessTokenAsync(string token);
Task SetTokensAsync(string accessToken, string refreshToken); Task SetTokensAsync(string accessToken, string refreshToken);
Task SetTwoFactorTokenAsync(string token, string email); Task SetTwoFactorTokenAsync(string token, string email);
bool TokenNeedsRefresh(int minutes = 5); bool TokenNeedsRefresh(int minutes = 5);

View File

@@ -1,31 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
namespace Bit.Core.Abstractions
{
public interface IUserService
{
Task<bool> CanAccessPremiumAsync();
Task ClearAsync();
Task ClearOrganizationsAsync(string userId);
Task<List<Organization>> GetAllOrganizationAsync();
Task<string> GetEmailAsync();
Task<KdfType?> GetKdfAsync();
Task<int?> GetKdfIterationsAsync();
Task<Organization> GetOrganizationAsync(string id);
Task<Organization> GetOrganizationByIdentifierAsync(string identifier);
Task<string> GetSecurityStampAsync();
Task<bool> GetEmailVerifiedAsync();
Task<bool> GetForcePasswordReset();
Task<string> GetUserIdAsync();
Task<bool> IsAuthenticatedAsync();
Task ReplaceOrganizationsAsync(Dictionary<string, OrganizationData> organizations);
Task SetInformationAsync(string userId, string email, KdfType kdf, int? kdfIterations);
Task SetSecurityStampAsync(string stamp);
Task SetEmailVerifiedAsync(bool emailVerified);
Task SetForcePasswordReset(bool forcePasswordReset);
}
}

View File

@@ -1,23 +1,22 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Models.Domain;
namespace Bit.Core.Abstractions namespace Bit.Core.Abstractions
{ {
public interface IVaultTimeoutService public interface IVaultTimeoutService
{ {
EncString PinProtectedKey { get; set; }
bool BiometricLocked { get; set; }
long? DelayLockAndLogoutMs { get; set; } long? DelayLockAndLogoutMs { get; set; }
Task CheckVaultTimeoutAsync(); Task CheckVaultTimeoutAsync();
Task ClearAsync(); Task<bool> ShouldTimeoutAsync(string userId = null);
Task<bool> IsLockedAsync(); Task ExecuteTimeoutActionAsync(string userId = null);
Task<Tuple<bool, bool>> IsPinLockSetAsync(); Task ClearAsync(string userId = null);
Task<bool> IsBiometricLockSetAsync(); Task<bool> IsLockedAsync(string userId = null);
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false); Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
Task LogOutAsync(); 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); Task SetVaultTimeoutOptionsAsync(int? timeout, string action);
Task<int?> GetVaultTimeout(); Task<int?> GetVaultTimeout(string userId = null);
} }
} }

View File

@@ -2,31 +2,19 @@
{ {
public static class Constants public static class Constants
{ {
public const int MaxAccounts = 5;
public const string AndroidAppProtocol = "androidapp://"; public const string AndroidAppProtocol = "androidapp://";
public const string iOSAppProtocol = "iosapp://"; public const string iOSAppProtocol = "iosapp://";
public static string SyncOnRefreshKey = "syncOnRefresh"; public static string StateKey = "state";
public static string VaultTimeoutKey = "lockOption"; public static string AppIdKey = "appId";
public static string VaultTimeoutActionKey = "vaultTimeoutAction"; public static string PreAuthEnvironmentUrlsKey = "preAuthEnvironmentUrls";
public static string LastActiveTimeKey = "lastActiveTime";
public static string BiometricUnlockKey = "fingerprintUnlock";
public static string ProtectedPin = "protectedPin";
public static string PinProtectedKey = "pinProtectedKey";
public static string DefaultUriMatch = "defaultUriMatch";
public static string DisableAutoTotpCopyKey = "disableAutoTotpCopy";
public static string EnvironmentUrlsKey = "environmentUrls";
public static string LastFileCacheClearKey = "lastFileCacheClear"; public static string LastFileCacheClearKey = "lastFileCacheClear";
public static string AutofillDisableSavePromptKey = "autofillDisableSavePrompt";
public static string AutofillBlacklistedUrisKey = "autofillBlacklistedUris";
public static string AutofillTileAdded = "autofillTileAdded"; public static string AutofillTileAdded = "autofillTileAdded";
public static string DisableFaviconKey = "disableFavicon";
public static string PushRegisteredTokenKey = "pushRegisteredToken"; public static string PushRegisteredTokenKey = "pushRegisteredToken";
public static string PushCurrentTokenKey = "pushCurrentToken"; public static string PushCurrentTokenKey = "pushCurrentToken";
public static string PushLastRegistrationDateKey = "pushLastRegistrationDate"; public static string PushLastRegistrationDateKey = "pushLastRegistrationDate";
public static string PushInitialPromptShownKey = "pushInitialPromptShown"; public static string PushInitialPromptShownKey = "pushInitialPromptShown";
public static string ThemeKey = "theme";
public static string ClearClipboardKey = "clearClipboard";
public static string LastBuildKey = "lastBuild"; public static string LastBuildKey = "lastBuild";
public static string OldUserIdKey = "userId";
public static string AddSitePromptShownKey = "addSitePromptShown"; public static string AddSitePromptShownKey = "addSitePromptShown";
public static string ClearCiphersCacheKey = "clearCiphersCache"; public static string ClearCiphersCacheKey = "clearCiphersCache";
public static string BiometricIntegrityKey = "biometricIntegrityState"; public static string BiometricIntegrityKey = "biometricIntegrityState";
@@ -38,11 +26,12 @@
public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown"; public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown";
public static string TriedV1Resync = "triedV1Resync"; public static string TriedV1Resync = "triedV1Resync";
public static string EventCollectionKey = "eventCollection"; public static string EventCollectionKey = "eventCollection";
public static string PreviousPageKey = "previousPage"; public static string RememberEmailKey = "rememberEmail";
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled"; public static string RememberedEmailKey = "rememberedEmail";
public static string InvalidUnlockAttempts = "invalidUnlockAttempts"; public static string RememberOrgIdentifierKey = "rememberOrgIdentifier";
public static string PasswordRepromptAutofillKey = "passwordRepromptAutofillKey"; public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
public static string PasswordVerifiedAutofillKey = "passwordVerifiedAutofillKey"; public static string AppExtensionStartedKey = "appExtensionStarted";
public static string AppExtensionActivatedKey = "appExtensionActivated";
public const int SelectFileRequestCode = 42; public const int SelectFileRequestCode = 42;
public const int SelectFilePermissionRequestCode = 43; public const int SelectFilePermissionRequestCode = 43;
public const int SaveFileRequestCode = 44; public const int SaveFileRequestCode = 44;
@@ -58,5 +47,43 @@
iOSAutoFillClearCiphersCacheKey, iOSAutoFillClearCiphersCacheKey,
iOSExtensionClearCiphersCacheKey iOSExtensionClearCiphersCacheKey
}; };
public static string CiphersKey(string userId) => $"ciphers_{userId}";
public static string FoldersKey(string userId) => $"folders_{userId}";
public static string CollectionsKey(string userId) => $"collections_{userId}";
public static string OrganizationsKey(string userId) => $"organizations_{userId}";
public static string LocalDataKey(string userId) => $"ciphersLocalData_{userId}";
public static string NeverDomainsKey(string userId) => $"neverDomains_{userId}";
public static string SendsKey(string userId) => $"sends_{userId}";
public static string PoliciesKey(string userId) => $"policies_{userId}";
public static string KeyKey(string userId) => $"key_{userId}";
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
public static string EncKeyKey(string userId) => $"encKey_{userId}";
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
public static string PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}";
public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}";
public static string LastActiveTimeKey(string userId) => $"lastActiveTime_{userId}";
public static string InvalidUnlockAttemptsKey(string userId) => $"invalidUnlockAttempts_{userId}";
public static string InlineAutofillEnabledKey(string userId) => $"inlineAutofillEnabled_{userId}";
public static string AutofillDisableSavePromptKey(string userId) => $"autofillDisableSavePrompt_{userId}";
public static string AutofillBlacklistedUrisKey(string userId) => $"autofillBlacklistedUris_{userId}";
public static string ClearClipboardKey(string userId) => $"clearClipboard_{userId}";
public static string SyncOnRefreshKey(string userId) => $"syncOnRefresh_{userId}";
public static string DisableFaviconKey(string userId) => $"disableFavicon_{userId}";
public static string DefaultUriMatchKey(string userId) => $"defaultUriMatch_{userId}";
public static string ThemeKey(string userId) => $"theme_{userId}";
public static string DisableAutoTotpCopyKey(string userId) => $"disableAutoTotpCopy_{userId}";
public static string PreviousPageKey(string userId) => $"previousPage_{userId}";
public static string PasswordRepromptAutofillKey(string userId) => $"passwordRepromptAutofillKey_{userId}";
public static string PasswordVerifiedAutofillKey(string userId) => $"passwordVerifiedAutofillKey_{userId}";
public static string SettingsKey(string userId) => $"settings_{userId}";
public static string UsesKeyConnectorKey(string userId) => $"usesKeyConnector_{userId}";
public static string ProtectedPinKey(string userId) => $"protectedPin_{userId}";
public static string ForcePasswordResetKey(string userId) => $"forcePasswordReset_{userId}";
public static string LastSyncKey(string userId) => $"lastSync_{userId}";
public static string BiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
} }
} }

View File

@@ -0,0 +1,10 @@
namespace Bit.Core.Enums
{
public enum AuthenticationStatus : byte
{
LoggedOut = 0,
Locked = 1,
Unlocked = 2,
Active = 3,
}
}

View File

@@ -0,0 +1,9 @@
namespace Bit.Core.Enums
{
public enum StorageLocation
{
Both = 0,
Disk = 1,
Memory = 2
}
}

View File

@@ -1,4 +1,4 @@
namespace Bit.App.Models namespace Bit.Core.Models.Data
{ {
public class PreviousPageInfo public class PreviousPageInfo
{ {

View File

@@ -0,0 +1,107 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Core.Models.Domain
{
public class Account : Domain
{
public AccountProfile Profile;
public AccountTokens Tokens;
public AccountSettings Settings;
public AccountKeys Keys;
public Account() { }
public Account(AccountProfile profile, AccountTokens tokens)
{
Profile = profile;
Tokens = tokens;
Settings = new AccountSettings();
Keys = new AccountKeys();
}
public Account(Account account)
{
// Copy constructor excludes Keys (for storage)
Profile = new AccountProfile(account.Profile);
Tokens = new AccountTokens(account.Tokens);
Settings = new AccountSettings(account.Settings);
}
public class AccountProfile
{
public AccountProfile() { }
public AccountProfile(AccountProfile copy)
{
if (copy == null)
{
return;
}
UserId = copy.UserId;
Email = copy.Email;
Stamp = copy.Stamp;
KdfType = copy.KdfType;
KdfIterations = copy.KdfIterations;
EmailVerified = copy.EmailVerified;
HasPremiumPersonally = copy.HasPremiumPersonally;
}
public string UserId;
public string Email;
public string Stamp;
public KdfType? KdfType;
public int? KdfIterations;
public bool? EmailVerified;
public bool? HasPremiumPersonally;
}
public class AccountTokens
{
public AccountTokens() { }
public AccountTokens(AccountTokens copy)
{
if (copy == null)
{
return;
}
AccessToken = copy.AccessToken;
RefreshToken = copy.RefreshToken;
}
public string AccessToken;
public string RefreshToken;
}
public class AccountSettings
{
public AccountSettings() { }
public AccountSettings(AccountSettings copy)
{
if (copy == null)
{
return;
}
EnvironmentUrls = copy.EnvironmentUrls;
PinProtected = copy.PinProtected;
VaultTimeout = copy.VaultTimeout;
VaultTimeoutAction = copy.VaultTimeoutAction;
}
public EnvironmentUrlData EnvironmentUrls;
public EncString PinProtected;
public int? VaultTimeout;
public string VaultTimeoutAction;
}
public class AccountKeys
{
public SymmetricCryptoKey Key;
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Bit.Core.Models.Domain
{
public class State : Domain
{
public Dictionary<string, Account> Accounts { get; set; }
public string ActiveUserId { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using Bit.Core.Enums;
namespace Bit.Core.Models.Domain
{
public class StorageOptions : Domain
{
public StorageLocation? StorageLocation { get; set; }
public bool? UseSecureStorage { get; set; }
public string UserId { get; set; }
public string Email { get; set; }
public bool? SkipTokenStorage { get; set; }
}
}

View File

@@ -0,0 +1,29 @@
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
namespace Bit.Core.Models.View
{
public class AccountView : View
{
public AccountView() { }
public AccountView(Account a = null)
{
if (a == null)
{
// null will render as "Add Account" row
return;
}
IsAccount = true;
UserId = a.Profile?.UserId;
Email = a.Profile?.Email;
Hostname = a.Settings?.EnvironmentUrls?.Base;
}
public bool IsAccount { get; set; }
public AuthenticationStatus? AuthStatus { get; set; }
public string UserId { get; set; }
public string Email { get; set; }
public string Hostname { get; set; }
}
}

View File

@@ -24,12 +24,12 @@ namespace Bit.Core.Services
private readonly HttpClient _httpClient = new HttpClient(); private readonly HttpClient _httpClient = new HttpClient();
private readonly ITokenService _tokenService; private readonly ITokenService _tokenService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly Func<bool, Task> _logoutCallbackAsync; private readonly Func<Tuple<string, bool, bool>, Task> _logoutCallbackAsync;
public ApiService( public ApiService(
ITokenService tokenService, ITokenService tokenService,
IPlatformUtilsService platformUtilsService, IPlatformUtilsService platformUtilsService,
Func<bool, Task> logoutCallbackAsync, Func<Tuple<string, bool, bool>, Task> logoutCallbackAsync,
string customUserAgent = null) string customUserAgent = null)
{ {
_tokenService = tokenService; _tokenService = tokenService;
@@ -698,7 +698,7 @@ namespace Bit.Core.Services
if (authed && ((tokenError && response.StatusCode == HttpStatusCode.BadRequest) || if (authed && ((tokenError && response.StatusCode == HttpStatusCode.BadRequest) ||
response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)) response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden))
{ {
await _logoutCallbackAsync(true); await _logoutCallbackAsync(new Tuple<string, bool, bool>(null, false, true));
return null; return null;
} }
try try

View File

@@ -6,33 +6,28 @@ namespace Bit.Core.Services
{ {
public class AppIdService : IAppIdService public class AppIdService : IAppIdService
{ {
private readonly IStorageService _storageService; private readonly IStateService _stateService;
public AppIdService(IStorageService storageService) public AppIdService(IStateService stateService)
{ {
_storageService = storageService; _stateService = stateService;
} }
public Task<string> GetAppIdAsync() public async Task<string> GetAppIdAsync()
{ {
return MakeAndGetAppIdAsync("appId"); var appId = await _stateService.GetAppIdAsync();
} if (appId != null)
public Task<string> GetAnonymousAppIdAsync()
{
return MakeAndGetAppIdAsync("anonymousAppId");
}
private async Task<string> MakeAndGetAppIdAsync(string key)
{
var existingId = await _storageService.GetAsync<string>(key);
if (existingId != null)
{ {
return existingId; return appId;
} }
var guid = Guid.NewGuid().ToString(); appId = MakeAppId();
await _storageService.SaveAsync(key, guid); await _stateService.SetAppIdAsync(appId);
return guid; return appId;
}
private string MakeAppId()
{
return Guid.NewGuid().ToString();
} }
} }
} }

View File

@@ -14,13 +14,12 @@ namespace Bit.Core.Services
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly ITokenService _tokenService; private readonly ITokenService _tokenService;
private readonly IAppIdService _appIdService; private readonly IAppIdService _appIdService;
private readonly II18nService _i18nService; private readonly II18nService _i18nService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IKeyConnectorService _keyConnectorService; private readonly IKeyConnectorService _keyConnectorService;
private readonly bool _setCryptoKeys; private readonly bool _setCryptoKeys;
@@ -30,7 +29,7 @@ namespace Bit.Core.Services
ICryptoService cryptoService, ICryptoService cryptoService,
ICryptoFunctionService cryptoFunctionService, ICryptoFunctionService cryptoFunctionService,
IApiService apiService, IApiService apiService,
IUserService userService, IStateService stateService,
ITokenService tokenService, ITokenService tokenService,
IAppIdService appIdService, IAppIdService appIdService,
II18nService i18nService, II18nService i18nService,
@@ -43,13 +42,12 @@ namespace Bit.Core.Services
_cryptoService = cryptoService; _cryptoService = cryptoService;
_cryptoFunctionService = cryptoFunctionService; _cryptoFunctionService = cryptoFunctionService;
_apiService = apiService; _apiService = apiService;
_userService = userService; _stateService = stateService;
_tokenService = tokenService; _tokenService = tokenService;
_appIdService = appIdService; _appIdService = appIdService;
_i18nService = i18nService; _i18nService = i18nService;
_platformUtilsService = platformUtilsService; _platformUtilsService = platformUtilsService;
_messagingService = messagingService; _messagingService = messagingService;
_vaultTimeoutService = vaultTimeoutService;
_keyConnectorService = keyConnectorService; _keyConnectorService = keyConnectorService;
_setCryptoKeys = setCryptoKeys; _setCryptoKeys = setCryptoKeys;
@@ -355,8 +353,24 @@ namespace Bit.Core.Services
await _tokenService.SetTwoFactorTokenAsync(tokenResponse.TwoFactorToken, email); await _tokenService.SetTwoFactorTokenAsync(tokenResponse.TwoFactorToken, email);
} }
await _tokenService.SetTokensAsync(tokenResponse.AccessToken, tokenResponse.RefreshToken); await _tokenService.SetTokensAsync(tokenResponse.AccessToken, tokenResponse.RefreshToken);
await _userService.SetInformationAsync(_tokenService.GetUserId(), _tokenService.GetEmail(), await _stateService.AddAccountAsync(
tokenResponse.Kdf, tokenResponse.KdfIterations); new Account(
new Account.AccountProfile()
{
UserId = _tokenService.GetUserId(),
Email = _tokenService.GetEmail(),
KdfType = tokenResponse.Kdf,
KdfIterations = tokenResponse.KdfIterations,
HasPremiumPersonally = _tokenService.GetPremium(),
},
new Account.AccountTokens()
{
AccessToken = tokenResponse.AccessToken,
RefreshToken = tokenResponse.RefreshToken,
}
)
);
_messagingService.Send("accountAdded");
if (_setCryptoKeys) if (_setCryptoKeys)
{ {
if (key != null) if (key != null)
@@ -430,7 +444,7 @@ namespace Bit.Core.Services
} }
_vaultTimeoutService.BiometricLocked = false; _stateService.BiometricLocked = false;
_messagingService.Send("loggedIn"); _messagingService.Send("loggedIn");
return result; return result;
} }

View File

@@ -19,15 +19,11 @@ namespace Bit.Core.Services
{ {
public class CipherService : ICipherService public class CipherService : ICipherService
{ {
private const string Keys_CiphersFormat = "ciphers_{0}";
private const string Keys_LocalData = "ciphersLocalData";
private const string Keys_NeverDomains = "neverDomains";
private readonly string[] _ignoredSearchTerms = new string[] { "com", "net", "org", "android", private readonly string[] _ignoredSearchTerms = new string[] { "com", "net", "org", "android",
"io", "co", "uk", "au", "nz", "fr", "de", "tv", "info", "app", "apps", "eu", "me", "dev", "jp", "mobile" }; "io", "co", "uk", "au", "nz", "fr", "de", "tv", "info", "app", "apps", "eu", "me", "dev", "jp", "mobile" };
private List<CipherView> _decryptedCipherCache; private List<CipherView> _decryptedCipherCache;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IFileUploadService _fileUploadService; private readonly IFileUploadService _fileUploadService;
@@ -45,7 +41,7 @@ namespace Bit.Core.Services
public CipherService( public CipherService(
ICryptoService cryptoService, ICryptoService cryptoService,
IUserService userService, IStateService stateService,
ISettingsService settingsService, ISettingsService settingsService,
IApiService apiService, IApiService apiService,
IFileUploadService fileUploadService, IFileUploadService fileUploadService,
@@ -56,7 +52,7 @@ namespace Bit.Core.Services
string[] allClearCipherCacheKeys) string[] allClearCipherCacheKeys)
{ {
_cryptoService = cryptoService; _cryptoService = cryptoService;
_userService = userService; _stateService = stateService;
_settingsService = settingsService; _settingsService = settingsService;
_apiService = apiService; _apiService = apiService;
_fileUploadService = fileUploadService; _fileUploadService = fileUploadService;
@@ -211,11 +207,8 @@ namespace Bit.Core.Services
public async Task<Cipher> GetAsync(string id) public async Task<Cipher> GetAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var localData = await _stateService.GetLocalDataAsync();
var localData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>( var ciphers = await _stateService.GetEncryptedCiphersAsync();
Keys_LocalData);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
string.Format(Keys_CiphersFormat, userId));
if (!ciphers?.ContainsKey(id) ?? true) if (!ciphers?.ContainsKey(id) ?? true)
{ {
return null; return null;
@@ -226,11 +219,8 @@ namespace Bit.Core.Services
public async Task<List<Cipher>> GetAllAsync() public async Task<List<Cipher>> GetAllAsync()
{ {
var userId = await _userService.GetUserIdAsync(); var localData = await _stateService.GetLocalDataAsync();
var localData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>( var ciphers = await _stateService.GetEncryptedCiphersAsync();
Keys_LocalData);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
string.Format(Keys_CiphersFormat, userId));
var response = ciphers?.Select(c => new Cipher(c.Value, false, var response = ciphers?.Select(c => new Cipher(c.Value, false,
localData?.ContainsKey(c.Key) ?? false ? localData[c.Key] : null)); localData?.ContainsKey(c.Key) ?? false ? localData[c.Key] : null));
return response?.ToList() ?? new List<Cipher>(); return response?.ToList() ?? new List<Cipher>();
@@ -347,7 +337,7 @@ namespace Bit.Core.Services
var others = new List<CipherView>(); var others = new List<CipherView>();
var ciphers = await ciphersTask; var ciphers = await ciphersTask;
var defaultMatch = (UriMatchType?)(await _storageService.GetAsync<int?>(Constants.DefaultUriMatch)); var defaultMatch = (UriMatchType?)(await _stateService.GetDefaultUriMatchAsync());
if (defaultMatch == null) if (defaultMatch == null)
{ {
defaultMatch = UriMatchType.Domain; defaultMatch = UriMatchType.Domain;
@@ -457,8 +447,7 @@ namespace Bit.Core.Services
public async Task UpdateLastUsedDateAsync(string id) public async Task UpdateLastUsedDateAsync(string id)
{ {
var ciphersLocalData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>( var ciphersLocalData = await _stateService.GetLocalDataAsync();
Keys_LocalData);
if (ciphersLocalData == null) if (ciphersLocalData == null)
{ {
ciphersLocalData = new Dictionary<string, Dictionary<string, object>>(); ciphersLocalData = new Dictionary<string, Dictionary<string, object>>();
@@ -476,7 +465,7 @@ namespace Bit.Core.Services
ciphersLocalData[id].Add("lastUsedDate", DateTime.UtcNow); ciphersLocalData[id].Add("lastUsedDate", DateTime.UtcNow);
} }
await _storageService.SaveAsync(Keys_LocalData, ciphersLocalData); await _stateService.SetLocalDataAsync(ciphersLocalData);
// Update cache // Update cache
if (DecryptedCipherCache == null) if (DecryptedCipherCache == null)
{ {
@@ -495,13 +484,13 @@ namespace Bit.Core.Services
{ {
return; return;
} }
var domains = await _storageService.GetAsync<HashSet<string>>(Keys_NeverDomains); var domains = await _stateService.GetNeverDomainsAsync();
if (domains == null) if (domains == null)
{ {
domains = new HashSet<string>(); domains = new HashSet<string>();
} }
domains.Add(domain); domains.Add(domain);
await _storageService.SaveAsync(Keys_NeverDomains, domains); await _stateService.SetNeverDomainsAsync(domains);
} }
public async Task SaveWithServerAsync(Cipher cipher) public async Task SaveWithServerAsync(Cipher cipher)
@@ -526,7 +515,7 @@ namespace Bit.Core.Services
var request = new CipherRequest(cipher); var request = new CipherRequest(cipher);
response = await _apiService.PutCipherAsync(cipher.Id, request); response = await _apiService.PutCipherAsync(cipher.Id, request);
} }
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
var data = new CipherData(response, userId, cipher.CollectionIds); var data = new CipherData(response, userId, cipher.CollectionIds);
await UpsertAsync(data); await UpsertAsync(data);
} }
@@ -550,7 +539,7 @@ namespace Bit.Core.Services
var encCipher = await EncryptAsync(cipher); var encCipher = await EncryptAsync(cipher);
var request = new CipherShareRequest(encCipher); var request = new CipherShareRequest(encCipher);
var response = await _apiService.PutShareCipherAsync(cipher.Id, request); var response = await _apiService.PutShareCipherAsync(cipher.Id, request);
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
var data = new CipherData(response, userId, collectionIds); var data = new CipherData(response, userId, collectionIds);
await UpsertAsync(data); await UpsertAsync(data);
} }
@@ -581,7 +570,7 @@ namespace Bit.Core.Services
response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, orgEncAttachmentKey); response = await LegacyServerAttachmentFileUploadAsync(cipher.Id, encFileName, encFileData, orgEncAttachmentKey);
} }
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
var cData = new CipherData(response, userId, cipher.CollectionIds); var cData = new CipherData(response, userId, cipher.CollectionIds);
await UpsertAsync(cData); await UpsertAsync(cData);
return new Cipher(cData); return new Cipher(cData);
@@ -602,16 +591,14 @@ namespace Bit.Core.Services
{ {
var request = new CipherCollectionsRequest(cipher.CollectionIds?.ToList()); var request = new CipherCollectionsRequest(cipher.CollectionIds?.ToList());
await _apiService.PutCipherCollectionsAsync(cipher.Id, request); await _apiService.PutCipherCollectionsAsync(cipher.Id, request);
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
var data = cipher.ToCipherData(userId); var data = cipher.ToCipherData(userId);
await UpsertAsync(data); await UpsertAsync(data);
} }
public async Task UpsertAsync(CipherData cipher) public async Task UpsertAsync(CipherData cipher)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var storageKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(storageKey);
if (ciphers == null) if (ciphers == null)
{ {
ciphers = new Dictionary<string, CipherData>(); ciphers = new Dictionary<string, CipherData>();
@@ -621,15 +608,13 @@ namespace Bit.Core.Services
ciphers.Add(cipher.Id, null); ciphers.Add(cipher.Id, null);
} }
ciphers[cipher.Id] = cipher; ciphers[cipher.Id] = cipher;
await _storageService.SaveAsync(storageKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
public async Task UpsertAsync(List<CipherData> cipher) public async Task UpsertAsync(List<CipherData> cipher)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var storageKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(storageKey);
if (ciphers == null) if (ciphers == null)
{ {
ciphers = new Dictionary<string, CipherData>(); ciphers = new Dictionary<string, CipherData>();
@@ -642,28 +627,25 @@ namespace Bit.Core.Services
} }
ciphers[c.Id] = c; ciphers[c.Id] = c;
} }
await _storageService.SaveAsync(storageKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
public async Task ReplaceAsync(Dictionary<string, CipherData> ciphers) public async Task ReplaceAsync(Dictionary<string, CipherData> ciphers)
{ {
var userId = await _userService.GetUserIdAsync(); await _stateService.SetEncryptedCiphersAsync(ciphers);
await _storageService.SaveAsync(string.Format(Keys_CiphersFormat, userId), ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
public async Task ClearAsync(string userId) public async Task ClearAsync(string userId)
{ {
await _storageService.RemoveAsync(string.Format(Keys_CiphersFormat, userId)); await _stateService.SetEncryptedCiphersAsync(null, userId);
await ClearCacheAsync(); await ClearCacheAsync();
} }
public async Task DeleteAsync(string id) public async Task DeleteAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var cipherKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
if (ciphers == null) if (ciphers == null)
{ {
return; return;
@@ -673,15 +655,13 @@ namespace Bit.Core.Services
return; return;
} }
ciphers.Remove(id); ciphers.Remove(id);
await _storageService.SaveAsync(cipherKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
public async Task DeleteAsync(List<string> ids) public async Task DeleteAsync(List<string> ids)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var cipherKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
if (ciphers == null) if (ciphers == null)
{ {
return; return;
@@ -694,7 +674,7 @@ namespace Bit.Core.Services
} }
ciphers.Remove(id); ciphers.Remove(id);
} }
await _storageService.SaveAsync(cipherKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
@@ -706,9 +686,7 @@ namespace Bit.Core.Services
public async Task DeleteAttachmentAsync(string id, string attachmentId) public async Task DeleteAttachmentAsync(string id, string attachmentId)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var cipherKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
if (ciphers == null || !ciphers.ContainsKey(id) || ciphers[id].Attachments == null) if (ciphers == null || !ciphers.ContainsKey(id) || ciphers[id].Attachments == null)
{ {
return; return;
@@ -718,7 +696,7 @@ namespace Bit.Core.Services
{ {
ciphers[id].Attachments.Remove(attachment); ciphers[id].Attachments.Remove(attachment);
} }
await _storageService.SaveAsync(cipherKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
@@ -771,9 +749,7 @@ namespace Bit.Core.Services
public async Task SoftDeleteWithServerAsync(string id) public async Task SoftDeleteWithServerAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var cipherKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
if (ciphers == null) if (ciphers == null)
{ {
return; return;
@@ -785,15 +761,13 @@ namespace Bit.Core.Services
await _apiService.PutDeleteCipherAsync(id); await _apiService.PutDeleteCipherAsync(id);
ciphers[id].DeletedDate = DateTime.UtcNow; ciphers[id].DeletedDate = DateTime.UtcNow;
await _storageService.SaveAsync(cipherKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }
public async Task RestoreWithServerAsync(string id) public async Task RestoreWithServerAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var ciphers = await _stateService.GetEncryptedCiphersAsync();
var cipherKey = string.Format(Keys_CiphersFormat, userId);
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
if (ciphers == null) if (ciphers == null)
{ {
return; return;
@@ -805,7 +779,7 @@ namespace Bit.Core.Services
var response = await _apiService.PutRestoreCipherAsync(id); var response = await _apiService.PutRestoreCipherAsync(id);
ciphers[id].DeletedDate = null; ciphers[id].DeletedDate = null;
ciphers[id].RevisionDate = response.RevisionDate; ciphers[id].RevisionDate = response.RevisionDate;
await _storageService.SaveAsync(cipherKey, ciphers); await _stateService.SetEncryptedCiphersAsync(ciphers);
await ClearCacheAsync(); await ClearCacheAsync();
} }

View File

@@ -13,24 +13,20 @@ namespace Bit.Core.Services
{ {
public class CollectionService : ICollectionService public class CollectionService : ICollectionService
{ {
private const string Keys_CollectionsFormat = "collections_{0}";
private const char NestingDelimiter = '/'; private const char NestingDelimiter = '/';
private List<CollectionView> _decryptedCollectionCache; private List<CollectionView> _decryptedCollectionCache;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IStorageService _storageService;
private readonly II18nService _i18nService; private readonly II18nService _i18nService;
public CollectionService( public CollectionService(
ICryptoService cryptoService, ICryptoService cryptoService,
IUserService userService, IStateService stateService,
IStorageService storageService,
II18nService i18nService) II18nService i18nService)
{ {
_cryptoService = cryptoService; _cryptoService = cryptoService;
_userService = userService; _stateService = stateService;
_storageService = storageService;
_i18nService = i18nService; _i18nService = i18nService;
} }
@@ -83,9 +79,7 @@ namespace Bit.Core.Services
public async Task<Collection> GetAsync(string id) public async Task<Collection> GetAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var collections = await _stateService.GetEncryptedCollectionsAsync();
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(
string.Format(Keys_CollectionsFormat, userId));
if (!collections?.ContainsKey(id) ?? true) if (!collections?.ContainsKey(id) ?? true)
{ {
return null; return null;
@@ -95,9 +89,7 @@ namespace Bit.Core.Services
public async Task<List<Collection>> GetAllAsync() public async Task<List<Collection>> GetAllAsync()
{ {
var userId = await _userService.GetUserIdAsync(); var collections = await _stateService.GetEncryptedCollectionsAsync();
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(
string.Format(Keys_CollectionsFormat, userId));
var response = collections?.Select(c => new Collection(c.Value)); var response = collections?.Select(c => new Collection(c.Value));
return response?.ToList() ?? new List<Collection>(); return response?.ToList() ?? new List<Collection>();
} }
@@ -148,9 +140,7 @@ namespace Bit.Core.Services
public async Task UpsertAsync(CollectionData collection) public async Task UpsertAsync(CollectionData collection)
{ {
var userId = await _userService.GetUserIdAsync(); var collections = await _stateService.GetEncryptedCollectionsAsync();
var storageKey = string.Format(Keys_CollectionsFormat, userId);
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(storageKey);
if (collections == null) if (collections == null)
{ {
collections = new Dictionary<string, CollectionData>(); collections = new Dictionary<string, CollectionData>();
@@ -160,15 +150,13 @@ namespace Bit.Core.Services
collections.Add(collection.Id, null); collections.Add(collection.Id, null);
} }
collections[collection.Id] = collection; collections[collection.Id] = collection;
await _storageService.SaveAsync(storageKey, collections); await _stateService.SetEncryptedCollectionsAsync(collections);
_decryptedCollectionCache = null; _decryptedCollectionCache = null;
} }
public async Task UpsertAsync(List<CollectionData> collection) public async Task UpsertAsync(List<CollectionData> collection)
{ {
var userId = await _userService.GetUserIdAsync(); var collections = await _stateService.GetEncryptedCollectionsAsync();
var storageKey = string.Format(Keys_CollectionsFormat, userId);
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(storageKey);
if (collections == null) if (collections == null)
{ {
collections = new Dictionary<string, CollectionData>(); collections = new Dictionary<string, CollectionData>();
@@ -181,34 +169,31 @@ namespace Bit.Core.Services
} }
collections[c.Id] = c; collections[c.Id] = c;
} }
await _storageService.SaveAsync(storageKey, collections); await _stateService.SetEncryptedCollectionsAsync(collections);
_decryptedCollectionCache = null; _decryptedCollectionCache = null;
} }
public async Task ReplaceAsync(Dictionary<string, CollectionData> collections) public async Task ReplaceAsync(Dictionary<string, CollectionData> collections)
{ {
var userId = await _userService.GetUserIdAsync(); await _stateService.SetEncryptedCollectionsAsync(collections);
await _storageService.SaveAsync(string.Format(Keys_CollectionsFormat, userId), collections);
_decryptedCollectionCache = null; _decryptedCollectionCache = null;
} }
public async Task ClearAsync(string userId) public async Task ClearAsync(string userId)
{ {
await _storageService.RemoveAsync(string.Format(Keys_CollectionsFormat, userId)); await _stateService.SetEncryptedCollectionsAsync(null, userId);
_decryptedCollectionCache = null; _decryptedCollectionCache = null;
} }
public async Task DeleteAsync(string id) public async Task DeleteAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var collections = await _stateService.GetEncryptedCollectionsAsync();
var collectionKey = string.Format(Keys_CollectionsFormat, userId);
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(collectionKey);
if (collections == null || !collections.ContainsKey(id)) if (collections == null || !collections.ContainsKey(id))
{ {
return; return;
} }
collections.Remove(id); collections.Remove(id);
await _storageService.SaveAsync(collectionKey, collections); await _stateService.SetEncryptedCollectionsAsync(collections);
_decryptedCollectionCache = null; _decryptedCollectionCache = null;
} }

View File

@@ -14,11 +14,9 @@ namespace Bit.Core.Services
{ {
public class CryptoService : ICryptoService public class CryptoService : ICryptoService
{ {
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly IStorageService _secureStorageService;
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private SymmetricCryptoKey _key;
private SymmetricCryptoKey _encKey; private SymmetricCryptoKey _encKey;
private SymmetricCryptoKey _legacyEtmKey; private SymmetricCryptoKey _legacyEtmKey;
private string _keyHash; private string _keyHash;
@@ -28,39 +26,31 @@ namespace Bit.Core.Services
private Task<SymmetricCryptoKey> _getEncKeysTask; private Task<SymmetricCryptoKey> _getEncKeysTask;
private Task<Dictionary<string, SymmetricCryptoKey>> _getOrgKeysTask; private Task<Dictionary<string, SymmetricCryptoKey>> _getOrgKeysTask;
private const string Keys_Key = "key";
private const string Keys_EncOrgKeys = "encOrgKeys";
private const string Keys_EncPrivateKey = "encPrivateKey";
private const string Keys_EncKey = "encKey";
private const string Keys_KeyHash = "keyHash";
public CryptoService( public CryptoService(
IStorageService storageService, IStateService stateService,
IStorageService secureStorageService,
ICryptoFunctionService cryptoFunctionService) ICryptoFunctionService cryptoFunctionService)
{ {
_storageService = storageService; _stateService = stateService;
_secureStorageService = secureStorageService;
_cryptoFunctionService = cryptoFunctionService; _cryptoFunctionService = cryptoFunctionService;
} }
public async Task SetKeyAsync(SymmetricCryptoKey key) public async Task SetKeyAsync(SymmetricCryptoKey key)
{ {
_key = key; await _stateService.SetKeyDecryptedAsync(key);
var option = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey); var option = await _stateService.GetVaultTimeoutAsync();
var biometric = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey); var biometric = await _stateService.GetBiometricUnlockAsync();
if (option.HasValue && !biometric.GetValueOrDefault()) if (option.HasValue && !biometric.GetValueOrDefault())
{ {
// If we have a lock option set, we do not store the key // If we have a lock option set, we do not store the key
return; return;
} }
await _secureStorageService.SaveAsync(Keys_Key, key?.KeyB64); await _stateService.SetKeyEncryptedAsync(key?.KeyB64);
} }
public async Task SetKeyHashAsync(string keyHash) public async Task SetKeyHashAsync(string keyHash)
{ {
_keyHash = keyHash; _keyHash = keyHash;
await _storageService.SaveAsync(Keys_KeyHash, keyHash); await _stateService.SetKeyHashAsync(keyHash);
} }
public async Task SetEncKeyAsync(string encKey) public async Task SetEncKeyAsync(string encKey)
@@ -69,7 +59,7 @@ namespace Bit.Core.Services
{ {
return; return;
} }
await _storageService.SaveAsync(Keys_EncKey, encKey); await _stateService.SetEncKeyEncryptedAsync(encKey);
_encKey = null; _encKey = null;
} }
@@ -79,7 +69,7 @@ namespace Bit.Core.Services
{ {
return; return;
} }
await _storageService.SaveAsync(Keys_EncPrivateKey, encPrivateKey); await _stateService.SetPrivateKeyEncryptedAsync(encPrivateKey);
_privateKey = null; _privateKey = null;
} }
@@ -87,21 +77,23 @@ namespace Bit.Core.Services
{ {
var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key); var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key);
_orgKeys = null; _orgKeys = null;
await _storageService.SaveAsync(Keys_EncOrgKeys, orgKeys); await _stateService.SetOrgKeysEncryptedAsync(orgKeys);
} }
public async Task<SymmetricCryptoKey> GetKeyAsync() public async Task<SymmetricCryptoKey> GetKeyAsync(string userId = null)
{ {
if (_key != null) var inMemoryKey = await _stateService.GetKeyDecryptedAsync(userId);
if (inMemoryKey != null)
{ {
return _key; return inMemoryKey;
} }
var key = await _secureStorageService.GetAsync<string>(Keys_Key); var key = await _stateService.GetKeyEncryptedAsync(userId);
if (key != null) if (key != null)
{ {
_key = new SymmetricCryptoKey(Convert.FromBase64String(key)); inMemoryKey = new SymmetricCryptoKey(Convert.FromBase64String(key));
await _stateService.SetKeyDecryptedAsync(inMemoryKey, userId);
} }
return _key; return inMemoryKey;
} }
public async Task<string> GetKeyHashAsync() public async Task<string> GetKeyHashAsync()
@@ -110,7 +102,7 @@ namespace Bit.Core.Services
{ {
return _keyHash; return _keyHash;
} }
var keyHash = await _storageService.GetAsync<string>(Keys_KeyHash); var keyHash = await _stateService.GetKeyHashAsync();
if (keyHash != null) if (keyHash != null)
{ {
_keyHash = keyHash; _keyHash = keyHash;
@@ -132,7 +124,7 @@ namespace Bit.Core.Services
{ {
try try
{ {
var encKey = await _storageService.GetAsync<string>(Keys_EncKey); var encKey = await _stateService.GetEncKeyEncryptedAsync();
if (encKey == null) if (encKey == null)
{ {
return null; return null;
@@ -200,7 +192,7 @@ namespace Bit.Core.Services
{ {
return _privateKey; return _privateKey;
} }
var encPrivateKey = await _storageService.GetAsync<string>(Keys_EncPrivateKey); var encPrivateKey = await _stateService.GetPrivateKeyEncryptedAsync();
if (encPrivateKey == null) if (encPrivateKey == null)
{ {
return null; return null;
@@ -238,7 +230,7 @@ namespace Bit.Core.Services
{ {
try try
{ {
var encOrgKeys = await _storageService.GetAsync<Dictionary<string, string>>(Keys_EncOrgKeys); var encOrgKeys = await _stateService.GetOrgKeysEncryptedAsync();
if (encOrgKeys == null) if (encOrgKeys == null)
{ {
return null; return null;
@@ -303,84 +295,95 @@ namespace Bit.Core.Services
return false; 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; return key != null;
} }
public async Task<bool> HasEncKeyAsync() public async Task<bool> HasEncKeyAsync()
{ {
var encKey = await _storageService.GetAsync<string>(Keys_EncKey); var encKey = await _stateService.GetEncKeyEncryptedAsync();
return encKey != null; return encKey != null;
} }
public async Task ClearKeyAsync() public async Task ClearKeyAsync(string userId = null)
{ {
_key = _legacyEtmKey = null; await _stateService.SetKeyDecryptedAsync(null, userId);
await _secureStorageService.RemoveAsync(Keys_Key); _legacyEtmKey = null;
await _stateService.SetKeyEncryptedAsync(null, userId);
} }
public async Task ClearKeyHashAsync() public async Task ClearKeyHashAsync(string userId = null)
{ {
_keyHash = null; _keyHash = null;
await _storageService.RemoveAsync(Keys_KeyHash); await _stateService.SetKeyHashAsync(null, userId);
} }
public async Task ClearEncKeyAsync(bool memoryOnly = false) public async Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null)
{ {
_encKey = null; _encKey = null;
if (!memoryOnly) if (!memoryOnly)
{ {
await _storageService.RemoveAsync(Keys_EncKey); await _stateService.SetEncKeyEncryptedAsync(null, userId);
} }
} }
public async Task ClearKeyPairAsync(bool memoryOnly = false) public async Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null)
{ {
_publicKey = _privateKey = null; _publicKey = _privateKey = null;
if (!memoryOnly) if (!memoryOnly)
{ {
await _storageService.RemoveAsync(Keys_EncPrivateKey); await _stateService.SetPrivateKeyEncryptedAsync(null, userId);
} }
} }
public async Task ClearOrgKeysAsync(bool memoryOnly = false) public async Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null)
{ {
_orgKeys = null; _orgKeys = null;
if (!memoryOnly) if (!memoryOnly)
{ {
await _storageService.RemoveAsync(Keys_EncOrgKeys); await _stateService.SetOrgKeysEncryptedAsync(null, userId);
} }
} }
public async Task ClearPinProtectedKeyAsync() public async Task ClearPinProtectedKeyAsync(string userId = null)
{ {
await _storageService.RemoveAsync(Constants.PinProtectedKey); await _stateService.SetPinProtectedAsync(null, userId);
} }
public async Task ClearKeysAsync() public void ClearCache()
{
_encKey = null;
_legacyEtmKey = null;
_keyHash = null;
_publicKey = null;
_privateKey = null;
_orgKeys = null;
}
public async Task ClearKeysAsync(string userId = null)
{ {
await Task.WhenAll(new Task[] await Task.WhenAll(new Task[]
{ {
ClearKeyAsync(), ClearKeyAsync(userId),
ClearKeyHashAsync(), ClearKeyHashAsync(userId),
ClearOrgKeysAsync(), ClearOrgKeysAsync(false, userId),
ClearEncKeyAsync(), ClearEncKeyAsync(false, userId),
ClearKeyPairAsync(), ClearKeyPairAsync(false, userId),
ClearPinProtectedKeyAsync() ClearPinProtectedKeyAsync(userId)
}); });
} }
public async Task ToggleKeyAsync() public async Task ToggleKeyAsync()
{ {
var key = await GetKeyAsync(); var key = await GetKeyAsync();
var option = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey); var option = await _stateService.GetVaultTimeoutAsync();
var biometric = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey); var biometric = await _stateService.GetBiometricUnlockAsync();
if (!biometric.GetValueOrDefault() && (option != null || option == 0)) if (!biometric.GetValueOrDefault() && (option != null || option == 0))
{ {
await ClearKeyAsync(); await ClearKeyAsync();
_key = key; await _stateService.SetKeyDecryptedAsync(key);
return; return;
} }
await SetKeyAsync(key); await SetKeyAsync(key);
@@ -415,7 +418,7 @@ namespace Bit.Core.Services
{ {
if (protectedKeyCs == null) if (protectedKeyCs == null)
{ {
var pinProtectedKey = await _storageService.GetAsync<string>(Constants.PinProtectedKey); var pinProtectedKey = await _stateService.GetPinProtectedAsync();
if (pinProtectedKey == null) if (pinProtectedKey == null)
{ {
throw new Exception("No PIN protected key found."); throw new Exception("No PIN protected key found.");

View File

@@ -9,14 +9,14 @@ namespace Bit.Core.Services
public class EnvironmentService : IEnvironmentService public class EnvironmentService : IEnvironmentService
{ {
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
public EnvironmentService( public EnvironmentService(
IApiService apiService, IApiService apiService,
IStorageService storageService) IStateService stateService)
{ {
_apiService = apiService; _apiService = apiService;
_storageService = storageService; _stateService = stateService;
} }
public string BaseUrl { get; set; } public string BaseUrl { get; set; }
@@ -42,7 +42,11 @@ namespace Bit.Core.Services
public async Task SetUrlsFromStorageAsync() public async Task SetUrlsFromStorageAsync()
{ {
var urls = await _storageService.GetAsync<EnvironmentUrlData>(Constants.EnvironmentUrlsKey); var urls = await _stateService.GetEnvironmentUrlsAsync();
if (urls == null)
{
urls = await _stateService.GetPreAuthEnvironmentUrlsAsync();
}
if (urls == null) if (urls == null)
{ {
urls = new EnvironmentUrlData(); urls = new EnvironmentUrlData();
@@ -72,7 +76,7 @@ namespace Bit.Core.Services
urls.Icons = FormatUrl(urls.Icons); urls.Icons = FormatUrl(urls.Icons);
urls.Notifications = FormatUrl(urls.Notifications); urls.Notifications = FormatUrl(urls.Notifications);
urls.Events = FormatUrl(urls.Events); urls.Events = FormatUrl(urls.Events);
await _storageService.SaveAsync(Constants.EnvironmentUrlsKey, urls); await _stateService.SetPreAuthEnvironmentUrlsAsync(urls);
BaseUrl = urls.Base; BaseUrl = urls.Base;
WebVaultUrl = urls.WebVault; WebVaultUrl = urls.WebVault;
ApiUrl = urls.Api; ApiUrl = urls.Api;

View File

@@ -12,31 +12,31 @@ namespace Bit.Core.Services
{ {
public class EventService : IEventService public class EventService : IEventService
{ {
private readonly IStorageService _storageService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IOrganizationService _organizationService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
public EventService( public EventService(
IStorageService storageService,
IApiService apiService, IApiService apiService,
IUserService userService, IStateService stateService,
IOrganizationService organizationService,
ICipherService cipherService) ICipherService cipherService)
{ {
_storageService = storageService;
_apiService = apiService; _apiService = apiService;
_userService = userService; _stateService = stateService;
_organizationService = organizationService;
_cipherService = cipherService; _cipherService = cipherService;
} }
public async Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false) public async Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false)
{ {
var authed = await _userService.IsAuthenticatedAsync(); var authed = await _stateService.IsAuthenticatedAsync();
if (!authed) if (!authed)
{ {
return; return;
} }
var organizations = await _userService.GetAllOrganizationAsync(); var organizations = await _organizationService.GetAllAsync();
if (organizations == null) if (organizations == null)
{ {
return; return;
@@ -54,7 +54,7 @@ namespace Bit.Core.Services
return; return;
} }
} }
var eventCollection = await _storageService.GetAsync<List<EventData>>(Constants.EventCollectionKey); var eventCollection = await _stateService.GetEventCollectionAsync();
if (eventCollection == null) if (eventCollection == null)
{ {
eventCollection = new List<EventData>(); eventCollection = new List<EventData>();
@@ -65,7 +65,7 @@ namespace Bit.Core.Services
CipherId = cipherId, CipherId = cipherId,
Date = DateTime.UtcNow Date = DateTime.UtcNow
}); });
await _storageService.SaveAsync(Constants.EventCollectionKey, eventCollection); await _stateService.SetEventCollectionAsync(eventCollection);
if (uploadImmediately) if (uploadImmediately)
{ {
await UploadEventsAsync(); await UploadEventsAsync();
@@ -74,12 +74,12 @@ namespace Bit.Core.Services
public async Task UploadEventsAsync() public async Task UploadEventsAsync()
{ {
var authed = await _userService.IsAuthenticatedAsync(); var authed = await _stateService.IsAuthenticatedAsync();
if (!authed) if (!authed)
{ {
return; return;
} }
var eventCollection = await _storageService.GetAsync<List<EventData>>(Constants.EventCollectionKey); var eventCollection = await _stateService.GetEventCollectionAsync();
if (eventCollection == null || !eventCollection.Any()) if (eventCollection == null || !eventCollection.Any())
{ {
return; return;
@@ -100,7 +100,7 @@ namespace Bit.Core.Services
public async Task ClearEventsAsync() public async Task ClearEventsAsync()
{ {
await _storageService.RemoveAsync(Constants.EventCollectionKey); await _stateService.SetEventCollectionAsync(null);
} }
} }
} }

View File

@@ -15,30 +15,25 @@ namespace Bit.Core.Services
{ {
public class FolderService : IFolderService public class FolderService : IFolderService
{ {
private const string Keys_CiphersFormat = "ciphers_{0}";
private const string Keys_FoldersFormat = "folders_{0}";
private const char NestingDelimiter = '/'; private const char NestingDelimiter = '/';
private List<FolderView> _decryptedFolderCache; private List<FolderView> _decryptedFolderCache;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IStorageService _storageService;
private readonly II18nService _i18nService; private readonly II18nService _i18nService;
private readonly ICipherService _cipherService; private readonly ICipherService _cipherService;
public FolderService( public FolderService(
ICryptoService cryptoService, ICryptoService cryptoService,
IUserService userService, IStateService stateService,
IApiService apiService, IApiService apiService,
IStorageService storageService,
II18nService i18nService, II18nService i18nService,
ICipherService cipherService) ICipherService cipherService)
{ {
_cryptoService = cryptoService; _cryptoService = cryptoService;
_userService = userService; _stateService = stateService;
_apiService = apiService; _apiService = apiService;
_storageService = storageService;
_i18nService = i18nService; _i18nService = i18nService;
_cipherService = cipherService; _cipherService = cipherService;
} }
@@ -60,9 +55,7 @@ namespace Bit.Core.Services
public async Task<Folder> GetAsync(string id) public async Task<Folder> GetAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var folders = await _stateService.GetEncryptedFoldersAsync();
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(
string.Format(Keys_FoldersFormat, userId));
if (!folders?.ContainsKey(id) ?? true) if (!folders?.ContainsKey(id) ?? true)
{ {
return null; return null;
@@ -72,9 +65,7 @@ namespace Bit.Core.Services
public async Task<List<Folder>> GetAllAsync() public async Task<List<Folder>> GetAllAsync()
{ {
var userId = await _userService.GetUserIdAsync(); var folders = await _stateService.GetEncryptedFoldersAsync();
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(
string.Format(Keys_FoldersFormat, userId));
var response = folders?.Select(f => new Folder(f.Value)); var response = folders?.Select(f => new Folder(f.Value));
return response?.ToList() ?? new List<Folder>(); return response?.ToList() ?? new List<Folder>();
} }
@@ -153,16 +144,14 @@ namespace Bit.Core.Services
{ {
response = await _apiService.PutFolderAsync(folder.Id, request); response = await _apiService.PutFolderAsync(folder.Id, request);
} }
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
var data = new FolderData(response, userId); var data = new FolderData(response, userId);
await UpsertAsync(data); await UpsertAsync(data);
} }
public async Task UpsertAsync(FolderData folder) public async Task UpsertAsync(FolderData folder)
{ {
var userId = await _userService.GetUserIdAsync(); var folders = await _stateService.GetEncryptedFoldersAsync();
var storageKey = string.Format(Keys_FoldersFormat, userId);
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(storageKey);
if (folders == null) if (folders == null)
{ {
folders = new Dictionary<string, FolderData>(); folders = new Dictionary<string, FolderData>();
@@ -172,15 +161,13 @@ namespace Bit.Core.Services
folders.Add(folder.Id, null); folders.Add(folder.Id, null);
} }
folders[folder.Id] = folder; folders[folder.Id] = folder;
await _storageService.SaveAsync(storageKey, folders); await _stateService.SetEncryptedFoldersAsync(folders);
_decryptedFolderCache = null; _decryptedFolderCache = null;
} }
public async Task UpsertAsync(List<FolderData> folder) public async Task UpsertAsync(List<FolderData> folder)
{ {
var userId = await _userService.GetUserIdAsync(); var folders = await _stateService.GetEncryptedFoldersAsync();
var storageKey = string.Format(Keys_FoldersFormat, userId);
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(storageKey);
if (folders == null) if (folders == null)
{ {
folders = new Dictionary<string, FolderData>(); folders = new Dictionary<string, FolderData>();
@@ -193,39 +180,35 @@ namespace Bit.Core.Services
} }
folders[f.Id] = f; folders[f.Id] = f;
} }
await _storageService.SaveAsync(storageKey, folders); await _stateService.SetEncryptedFoldersAsync(folders);
_decryptedFolderCache = null; _decryptedFolderCache = null;
} }
public async Task ReplaceAsync(Dictionary<string, FolderData> folders) public async Task ReplaceAsync(Dictionary<string, FolderData> folders)
{ {
var userId = await _userService.GetUserIdAsync(); await _stateService.SetEncryptedFoldersAsync(folders);
await _storageService.SaveAsync(string.Format(Keys_FoldersFormat, userId), folders);
_decryptedFolderCache = null; _decryptedFolderCache = null;
} }
public async Task ClearAsync(string userId) public async Task ClearAsync(string userId)
{ {
await _storageService.RemoveAsync(string.Format(Keys_FoldersFormat, userId)); await _stateService.SetEncryptedFoldersAsync(null, userId);
_decryptedFolderCache = null; _decryptedFolderCache = null;
} }
public async Task DeleteAsync(string id) public async Task DeleteAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var folders = await _stateService.GetEncryptedFoldersAsync();
var folderKey = string.Format(Keys_FoldersFormat, userId);
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(folderKey);
if (folders == null || !folders.ContainsKey(id)) if (folders == null || !folders.ContainsKey(id))
{ {
return; return;
} }
folders.Remove(id); folders.Remove(id);
await _storageService.SaveAsync(folderKey, folders); await _stateService.SetEncryptedFoldersAsync(folders);
_decryptedFolderCache = null; _decryptedFolderCache = null;
// Items in a deleted folder are re-assigned to "No Folder" // Items in a deleted folder are re-assigned to "No Folder"
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>( var ciphers = await _stateService.GetEncryptedCiphersAsync();
string.Format(Keys_CiphersFormat, userId));
if (ciphers != null) if (ciphers != null)
{ {
var updates = new List<CipherData>(); var updates = new List<CipherData>();

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
@@ -9,24 +8,20 @@ namespace Bit.Core.Services
{ {
public class KeyConnectorService : IKeyConnectorService public class KeyConnectorService : IKeyConnectorService
{ {
private const string Keys_UsesKeyConnector = "usesKeyConnector"; private readonly IStateService _stateService;
private readonly IUserService _userService;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IStorageService _storageService;
private readonly ITokenService _tokenService; private readonly ITokenService _tokenService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IOrganizationService _organizationService;
private bool? _usesKeyConnector; public KeyConnectorService(IStateService stateService, ICryptoService cryptoService,
ITokenService tokenService, IApiService apiService, OrganizationService organizationService)
public KeyConnectorService(IUserService userService, ICryptoService cryptoService,
IStorageService storageService, ITokenService tokenService, IApiService apiService)
{ {
_userService = userService; _stateService = stateService;
_cryptoService = cryptoService; _cryptoService = cryptoService;
_storageService = storageService;
_tokenService = tokenService; _tokenService = tokenService;
_apiService = apiService; _apiService = apiService;
_organizationService = organizationService;
} }
public async Task GetAndSetKey(string url) public async Task GetAndSetKey(string url)
@@ -46,23 +41,17 @@ namespace Bit.Core.Services
public async Task SetUsesKeyConnector(bool usesKeyConnector) public async Task SetUsesKeyConnector(bool usesKeyConnector)
{ {
_usesKeyConnector = usesKeyConnector; await _stateService.SetUsesKeyConnectorAsync(usesKeyConnector);
await _storageService.SaveAsync(Keys_UsesKeyConnector, usesKeyConnector);
} }
public async Task<bool> GetUsesKeyConnector() public async Task<bool> GetUsesKeyConnector()
{ {
if (!_usesKeyConnector.HasValue) return await _stateService.GetUsesKeyConnectorAsync();
{
_usesKeyConnector = await _storageService.GetAsync<bool>(Keys_UsesKeyConnector);
}
return _usesKeyConnector.Value;
} }
public async Task<Organization> GetManagingOrganization() public async Task<Organization> GetManagingOrganization()
{ {
var orgs = await _userService.GetAllOrganizationAsync(); var orgs = await _organizationService.GetAllAsync();
return orgs.Find(o => return orgs.Find(o =>
o.UsesKeyConnector && o.UsesKeyConnector &&
!o.IsAdmin); !o.IsAdmin);
@@ -88,7 +77,7 @@ namespace Bit.Core.Services
public async Task<bool> UserNeedsMigration() public async Task<bool> UserNeedsMigration()
{ {
var loggedInUsingSso = _tokenService.GetIsExternal(); var loggedInUsingSso = await _tokenService.GetIsExternal();
var requiredByOrganization = await GetManagingOrganization() != null; var requiredByOrganization = await GetManagingOrganization() != null;
var userIsNotUsingKeyConnector = !await GetUsesKeyConnector(); var userIsNotUsingKeyConnector = !await GetUsesKeyConnector();

View File

@@ -0,0 +1,55 @@
using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public class OrganizationService : IOrganizationService
{
private readonly IStateService _stateService;
public OrganizationService(IStateService stateService)
{
_stateService = stateService;
}
public async Task<Organization> GetAsync(string id)
{
var organizations = await _stateService.GetOrganizationsAsync();
if (organizations == null || !organizations.ContainsKey(id))
{
return null;
}
return new Organization(organizations[id]);
}
public async Task<Organization> GetByIdentifierAsync(string identifier)
{
var organizations = await GetAllAsync();
if (organizations == null || organizations.Count == 0)
{
return null;
}
return organizations.FirstOrDefault(o => o.Identifier == identifier);
}
public async Task<List<Organization>> GetAllAsync(string userId = null)
{
var organizations = await _stateService.GetOrganizationsAsync(userId);
return organizations?.Select(o => new Organization(o.Value)).ToList() ?? new List<Organization>();
}
public async Task ReplaceAsync(Dictionary<string, OrganizationData> organizations)
{
await _stateService.SetOrganizationsAsync(organizations);
}
public async Task ClearAllAsync(string userId)
{
await _stateService.SetOrganizationsAsync(null, userId);
}
}
}

View File

@@ -14,8 +14,6 @@ namespace Bit.Core.Services
{ {
public class PasswordGenerationService : IPasswordGenerationService public class PasswordGenerationService : IPasswordGenerationService
{ {
private const string Keys_Options = "passwordGenerationOptions";
private const string Keys_History = "generatedPasswordHistory";
private const int MaxPasswordsInHistory = 100; private const int MaxPasswordsInHistory = 100;
private const string LowercaseCharSet = "abcdefghijkmnopqrstuvwxyz"; private const string LowercaseCharSet = "abcdefghijkmnopqrstuvwxyz";
private const string UppercaseCharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ"; private const string UppercaseCharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ";
@@ -23,7 +21,7 @@ namespace Bit.Core.Services
private const string SpecialCharSet = "!@#$%^&*"; private const string SpecialCharSet = "!@#$%^&*";
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IStorageService _storageService; private readonly IStateService _stateService;
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private PasswordGenerationOptions _defaultOptions = new PasswordGenerationOptions(true); private PasswordGenerationOptions _defaultOptions = new PasswordGenerationOptions(true);
@@ -32,12 +30,12 @@ namespace Bit.Core.Services
public PasswordGenerationService( public PasswordGenerationService(
ICryptoService cryptoService, ICryptoService cryptoService,
IStorageService storageService, IStateService stateService,
ICryptoFunctionService cryptoFunctionService, ICryptoFunctionService cryptoFunctionService,
IPolicyService policyService) IPolicyService policyService)
{ {
_cryptoService = cryptoService; _cryptoService = cryptoService;
_storageService = storageService; _stateService = stateService;
_cryptoFunctionService = cryptoFunctionService; _cryptoFunctionService = cryptoFunctionService;
_policyService = policyService; _policyService = policyService;
} }
@@ -160,6 +158,12 @@ namespace Bit.Core.Services
return password.ToString(); return password.ToString();
} }
public void ClearCache()
{
_optionsCache = null;
_history = null;
}
public async Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options) public async Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options)
{ {
options.Merge(_defaultOptions); options.Merge(_defaultOptions);
@@ -204,7 +208,7 @@ namespace Bit.Core.Services
{ {
if (_optionsCache == null) if (_optionsCache == null)
{ {
var options = await _storageService.GetAsync<PasswordGenerationOptions>(Keys_Options); var options = await _stateService.GetPasswordGenerationOptionsAsync();
if (options == null) if (options == null)
{ {
_optionsCache = _defaultOptions; _optionsCache = _defaultOptions;
@@ -432,7 +436,7 @@ namespace Bit.Core.Services
public async Task SaveOptionsAsync(PasswordGenerationOptions options) public async Task SaveOptionsAsync(PasswordGenerationOptions options)
{ {
await _storageService.SaveAsync(Keys_Options, options); await _stateService.SetPasswordGenerationOptionsAsync(options);
_optionsCache = options; _optionsCache = options;
} }
@@ -445,7 +449,7 @@ namespace Bit.Core.Services
} }
if (_history == null) if (_history == null)
{ {
var encrypted = await _storageService.GetAsync<List<GeneratedPasswordHistory>>(Keys_History); var encrypted = await _stateService.GetEncryptedPasswordGenerationHistory();
_history = await DecryptHistoryAsync(encrypted); _history = await DecryptHistoryAsync(encrypted);
} }
return _history ?? new List<GeneratedPasswordHistory>(); return _history ?? new List<GeneratedPasswordHistory>();
@@ -473,13 +477,13 @@ namespace Bit.Core.Services
} }
var newHistory = await EncryptHistoryAsync(currentHistory); var newHistory = await EncryptHistoryAsync(currentHistory);
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
await _storageService.SaveAsync(Keys_History, newHistory); await _stateService.SetEncryptedPasswordGenerationHistoryAsync(newHistory);
} }
public async Task ClearAsync() public async Task ClearAsync(string userId = null)
{ {
_history = new List<GeneratedPasswordHistory>(); _history = new List<GeneratedPasswordHistory>();
await _storageService.RemoveAsync(Keys_History); await _stateService.SetEncryptedPasswordGenerationHistoryAsync(null, userId);
} }
public Result PasswordStrength(string password, List<string> userInputs = null) public Result PasswordStrength(string password, List<string> userInputs = null)

View File

@@ -12,19 +12,17 @@ namespace Bit.Core.Services
{ {
public class PolicyService : IPolicyService public class PolicyService : IPolicyService
{ {
private const string Keys_PoliciesPrefix = "policies_{0}"; private readonly IStateService _stateService;
private readonly IOrganizationService _organizationService;
private readonly IStorageService _storageService;
private readonly IUserService _userService;
private IEnumerable<Policy> _policyCache; private IEnumerable<Policy> _policyCache;
public PolicyService( public PolicyService(
IStorageService storageService, IStateService stateService,
IUserService userService) IOrganizationService organizationService)
{ {
_storageService = storageService; _stateService = stateService;
_userService = userService; _organizationService = organizationService;
} }
public void ClearCache() public void ClearCache()
@@ -36,9 +34,7 @@ namespace Bit.Core.Services
{ {
if (_policyCache == null) if (_policyCache == null)
{ {
var userId = await _userService.GetUserIdAsync(); var policies = await _stateService.GetEncryptedPoliciesAsync();
var policies = await _storageService.GetAsync<Dictionary<string, PolicyData>>(
string.Format(Keys_PoliciesPrefix, userId));
if (policies == null) if (policies == null)
{ {
return null; return null;
@@ -58,14 +54,13 @@ namespace Bit.Core.Services
public async Task Replace(Dictionary<string, PolicyData> policies) public async Task Replace(Dictionary<string, PolicyData> policies)
{ {
var userId = await _userService.GetUserIdAsync(); await _stateService.SetEncryptedPoliciesAsync(policies);
await _storageService.SaveAsync(string.Format(Keys_PoliciesPrefix, userId), policies);
_policyCache = null; _policyCache = null;
} }
public async Task Clear(string userId) public async Task Clear(string userId)
{ {
await _storageService.RemoveAsync(string.Format(Keys_PoliciesPrefix, userId)); await _stateService.SetEncryptedPoliciesAsync(null, userId);
_policyCache = null; _policyCache = null;
} }
@@ -205,7 +200,7 @@ namespace Bit.Core.Services
{ {
return false; return false;
} }
var organizations = await _userService.GetAllOrganizationAsync(); var organizations = await _organizationService.GetAllAsync();
IEnumerable<Policy> filteredPolicies; IEnumerable<Policy> filteredPolicies;

View File

@@ -20,9 +20,8 @@ namespace Bit.Core.Services
{ {
private List<SendView> _decryptedSendsCache; private List<SendView> _decryptedSendsCache;
private readonly ICryptoService _cryptoService; private readonly ICryptoService _cryptoService;
private readonly IUserService _userService; private readonly IStateService _stateService;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IStorageService _storageService;
private readonly II18nService _i18nService; private readonly II18nService _i18nService;
private readonly ICryptoFunctionService _cryptoFunctionService; private readonly ICryptoFunctionService _cryptoFunctionService;
private Task<List<SendView>> _getAllDecryptedTask; private Task<List<SendView>> _getAllDecryptedTask;
@@ -30,27 +29,23 @@ namespace Bit.Core.Services
public SendService( public SendService(
ICryptoService cryptoService, ICryptoService cryptoService,
IUserService userService, IStateService stateService,
IApiService apiService, IApiService apiService,
IFileUploadService fileUploadService, IFileUploadService fileUploadService,
IStorageService storageService,
II18nService i18nService, II18nService i18nService,
ICryptoFunctionService cryptoFunctionService) ICryptoFunctionService cryptoFunctionService)
{ {
_cryptoService = cryptoService; _cryptoService = cryptoService;
_userService = userService; _stateService = stateService;
_apiService = apiService; _apiService = apiService;
_fileUploadService = fileUploadService; _fileUploadService = fileUploadService;
_storageService = storageService;
_i18nService = i18nService; _i18nService = i18nService;
_cryptoFunctionService = cryptoFunctionService; _cryptoFunctionService = cryptoFunctionService;
} }
public static string GetSendKey(string userId) => string.Format("sends_{0}", userId);
public async Task ClearAsync(string userId) public async Task ClearAsync(string userId)
{ {
await _storageService.RemoveAsync(GetSendKey(userId)); await _stateService.SetEncryptedSendsAsync(null, userId);
ClearCache(); ClearCache();
} }
@@ -58,8 +53,7 @@ namespace Bit.Core.Services
public async Task DeleteAsync(params string[] ids) public async Task DeleteAsync(params string[] ids)
{ {
var userId = await _userService.GetUserIdAsync(); var sends = await _stateService.GetEncryptedSendsAsync();
var sends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId));
if (sends == null) if (sends == null)
{ {
@@ -71,7 +65,7 @@ namespace Bit.Core.Services
sends.Remove(id); sends.Remove(id);
} }
await _storageService.SaveAsync(GetSendKey(userId), sends); await _stateService.SetEncryptedSendsAsync(sends);
ClearCache(); ClearCache();
} }
@@ -138,8 +132,7 @@ namespace Bit.Core.Services
public async Task<List<Send>> GetAllAsync() public async Task<List<Send>> GetAllAsync()
{ {
var userId = await _userService.GetUserIdAsync(); var sends = await _stateService.GetEncryptedSendsAsync();
var sends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId));
return sends?.Select(kvp => new Send(kvp.Value)).ToList() ?? new List<Send>(); return sends?.Select(kvp => new Send(kvp.Value)).ToList() ?? new List<Send>();
} }
@@ -179,8 +172,7 @@ namespace Bit.Core.Services
public async Task<Send> GetAsync(string id) public async Task<Send> GetAsync(string id)
{ {
var userId = await _userService.GetUserIdAsync(); var sends = await _stateService.GetEncryptedSendsAsync();
var sends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId));
if (sends == null || !sends.ContainsKey(id)) if (sends == null || !sends.ContainsKey(id))
{ {
@@ -192,8 +184,7 @@ namespace Bit.Core.Services
public async Task ReplaceAsync(Dictionary<string, SendData> sends) public async Task ReplaceAsync(Dictionary<string, SendData> sends)
{ {
var userId = await _userService.GetUserIdAsync(); await _stateService.SetEncryptedSendsAsync(sends);
await _storageService.SaveAsync(GetSendKey(userId), sends);
_decryptedSendsCache = null; _decryptedSendsCache = null;
} }
@@ -237,7 +228,7 @@ namespace Bit.Core.Services
response = await _apiService.PutSendAsync(send.Id, request); response = await _apiService.PutSendAsync(send.Id, request);
} }
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
await UpsertAsync(new SendData(response, userId)); await UpsertAsync(new SendData(response, userId));
return response.Id; return response.Id;
} }
@@ -255,8 +246,7 @@ namespace Bit.Core.Services
public async Task UpsertAsync(params SendData[] sends) public async Task UpsertAsync(params SendData[] sends)
{ {
var userId = await _userService.GetUserIdAsync(); var knownSends = await _stateService.GetEncryptedSendsAsync() ??
var knownSends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId)) ??
new Dictionary<string, SendData>(); new Dictionary<string, SendData>();
foreach (var send in sends) foreach (var send in sends)
@@ -264,14 +254,14 @@ namespace Bit.Core.Services
knownSends[send.Id] = send; knownSends[send.Id] = send;
} }
await _storageService.SaveAsync(GetSendKey(userId), knownSends); await _stateService.SetEncryptedSendsAsync(knownSends);
_decryptedSendsCache = null; _decryptedSendsCache = null;
} }
public async Task RemovePasswordWithServerAsync(string id) public async Task RemovePasswordWithServerAsync(string id)
{ {
var response = await _apiService.PutSendRemovePasswordAsync(id); var response = await _apiService.PutSendRemovePasswordAsync(id);
var userId = await _userService.GetUserIdAsync(); var userId = await _stateService.GetActiveUserIdAsync();
await UpsertAsync(new SendData(response, userId)); await UpsertAsync(new SendData(response, userId));
} }

Some files were not shown because too many files have changed in this diff Show More