mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
19 Commits
iOS
...
fix-avatar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4e599f680 | ||
|
|
d41e1a0bc3 | ||
|
|
e717992b2b | ||
|
|
d52a1bf668 | ||
|
|
796cf6dc25 | ||
|
|
59e7969856 | ||
|
|
afecc1f569 | ||
|
|
a3922f7fbb | ||
|
|
7e288c62f9 | ||
|
|
ed2bb9b94e | ||
|
|
1c6a8964fe | ||
|
|
6aa96b8964 | ||
|
|
f6d4b0a443 | ||
|
|
e20ae26808 | ||
|
|
e3a8d3bb55 | ||
|
|
65739489a7 | ||
|
|
120fb70039 | ||
|
|
d1ece79d2e | ||
|
|
ebae2585f6 |
@@ -10,7 +10,6 @@ using Android.Views;
|
||||
using Android.Views.Accessibility;
|
||||
using Android.Widget;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -25,7 +24,7 @@ namespace Bit.Droid.Accessibility
|
||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||
|
||||
private IStorageService _storageService;
|
||||
private IStateService _stateService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private DateTime? _lastSettingsReload = null;
|
||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||
@@ -444,9 +443,9 @@ namespace Bit.Droid.Accessibility
|
||||
|
||||
private void LoadServices()
|
||||
{
|
||||
if (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
if (_broadcasterService == null)
|
||||
{
|
||||
@@ -460,12 +459,12 @@ namespace Bit.Droid.Accessibility
|
||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||
{
|
||||
_lastSettingsReload = now;
|
||||
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
if (uris != null)
|
||||
{
|
||||
_blacklistedUris = new HashSet<string>(uris);
|
||||
}
|
||||
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
||||
var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
|
||||
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,8 @@
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
||||
<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\ic_launcher_foreground.xml" />
|
||||
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
||||
|
||||
@@ -22,9 +22,8 @@ namespace Bit.Droid.Autofill
|
||||
{
|
||||
private ICipherService _cipherService;
|
||||
private IVaultTimeoutService _vaultTimeoutService;
|
||||
private IStorageService _storageService;
|
||||
private IPolicyService _policyService;
|
||||
private IUserService _userService;
|
||||
private IStateService _stateService;
|
||||
|
||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||
FillCallback callback)
|
||||
@@ -38,18 +37,18 @@ namespace Bit.Droid.Autofill
|
||||
var parser = new Parser(structure, ApplicationContext);
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||
var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
|
||||
if (_vaultTimeoutService == null)
|
||||
{
|
||||
@@ -81,12 +80,12 @@ namespace Bit.Droid.Autofill
|
||||
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())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -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) &&
|
||||
FieldCollection != null && FieldCollection.Fillable;
|
||||
if (fillable)
|
||||
{
|
||||
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
||||
var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
|
||||
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
||||
{
|
||||
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Bit.Droid
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IMessagingService _messagingService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
private IUserService _userService;
|
||||
private IStateService _stateService;
|
||||
private IAppIdService _appIdService;
|
||||
private IEventService _eventService;
|
||||
private PendingIntent _eventUploadPendingIntent;
|
||||
@@ -54,7 +54,7 @@ namespace Bit.Droid
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Bit.Droid
|
||||
}
|
||||
|
||||
#if !FDROID
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
||||
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||
var appCenterTask = appCenterHelper.InitAsync();
|
||||
#endif
|
||||
|
||||
@@ -373,7 +373,7 @@ namespace Bit.Droid
|
||||
{
|
||||
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
|
||||
}
|
||||
|
||||
private void ExitApp()
|
||||
|
||||
@@ -97,13 +97,14 @@ namespace Bit.Droid
|
||||
var secureStorageService = new SecureStorageService();
|
||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||
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"));
|
||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||
broadcasterService);
|
||||
var biometricService = new BiometricService();
|
||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
|
||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||
|
||||
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
||||
@@ -113,7 +114,8 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
||||
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
||||
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<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||
@@ -132,7 +134,7 @@ namespace Bit.Droid
|
||||
ServiceContainer.Register<IPushNotificationListenerService>(
|
||||
"pushNotificationListenerService", notificationListenerService);
|
||||
var androidPushNotificationService = new AndroidPushNotificationService(
|
||||
mobileStorageService, notificationListenerService);
|
||||
stateService, notificationListenerService);
|
||||
ServiceContainer.Register<IPushNotificationService>(
|
||||
"pushNotificationService", androidPushNotificationService);
|
||||
#endif
|
||||
@@ -148,10 +150,6 @@ namespace Bit.Droid
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
|
||||
{
|
||||
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");
|
||||
|
||||
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
||||
await stateService.SetPushRegisteredTokenAsync(token);
|
||||
await pushNotificationService.RegisterAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Utilities;
|
||||
@@ -14,9 +13,10 @@ namespace Bit.Droid.Receivers
|
||||
{
|
||||
public override async void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService);
|
||||
await AppHelpers.PerformUpdateTasksAsync(
|
||||
ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||
ServiceContainer.Resolve<IStateService>("stateService"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
src/Android/Resources/drawable/cog_environment.xml
Normal file
9
src/Android/Resources/drawable/cog_environment.xml
Normal 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>
|
||||
@@ -3,7 +3,6 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using AndroidX.Core.App;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -11,14 +10,14 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class AndroidPushNotificationService : IPushNotificationService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
||||
|
||||
public AndroidPushNotificationService(
|
||||
IStorageService storageService,
|
||||
IStateService stateService,
|
||||
IPushNotificationListenerService pushNotificationListenerService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
_pushNotificationListenerService = pushNotificationListenerService;
|
||||
}
|
||||
|
||||
@@ -26,12 +25,12 @@ namespace Bit.Droid.Services
|
||||
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);
|
||||
return await _stateService.GetPushCurrentTokenAsync();
|
||||
}
|
||||
|
||||
public async Task RegisterAsync()
|
||||
{
|
||||
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
|
||||
var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
|
||||
var currentToken = await GetTokenAsync();
|
||||
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||
{
|
||||
@@ -39,7 +38,7 @@ namespace Bit.Droid.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
||||
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class ClipboardService : IClipboardService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
|
||||
|
||||
public ClipboardService(IStorageService storageService)
|
||||
public ClipboardService(IStateService stateService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
|
||||
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
||||
@@ -39,7 +39,7 @@ namespace Bit.Droid.Services
|
||||
if (clearMs < 0)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
clearMs = clearSeconds.Value * 1000;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
public class DeviceActionService : IDeviceActionService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly Func<IEventService> _eventServiceFunc;
|
||||
@@ -43,12 +43,12 @@ namespace Bit.Droid.Services
|
||||
private string _userAgent;
|
||||
|
||||
public DeviceActionService(
|
||||
IStorageService storageService,
|
||||
IStateService stateService,
|
||||
IMessagingService messagingService,
|
||||
IBroadcasterService broadcasterService,
|
||||
Func<IEventService> eventServiceFunc)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
_messagingService = messagingService;
|
||||
_broadcasterService = broadcasterService;
|
||||
_eventServiceFunc = eventServiceFunc;
|
||||
@@ -250,7 +250,7 @@ namespace Bit.Droid.Services
|
||||
try
|
||||
{
|
||||
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
||||
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
||||
await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
@@ -833,9 +833,8 @@ namespace Bit.Droid.Services
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||
{
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
||||
var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||
{
|
||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
|
||||
@@ -4,7 +4,6 @@ using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Service.QuickSettings;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Accessibility;
|
||||
@@ -18,7 +17,7 @@ namespace Bit.Droid.Tile
|
||||
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
||||
public class AutofillTileService : TileService
|
||||
{
|
||||
private IStorageService _storageService;
|
||||
private IStateService _stateService;
|
||||
|
||||
public override void OnTileAdded()
|
||||
{
|
||||
@@ -59,11 +58,11 @@ namespace Bit.Droid.Tile
|
||||
private void SetTileAdded(bool 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()
|
||||
|
||||
@@ -12,22 +12,22 @@ namespace Bit.Droid.Utilities
|
||||
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
||||
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
private string _userId;
|
||||
private string _appId;
|
||||
|
||||
public AppCenterHelper(
|
||||
IAppIdService appIdService,
|
||||
IUserService userService)
|
||||
IStateService stateService)
|
||||
{
|
||||
_appIdService = appIdService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_userId = await _userService.GetUserIdAsync();
|
||||
_userId = await _stateService.GetActiveUserIdAsync();
|
||||
_appId = await _appIdService.GetAppIdAsync();
|
||||
|
||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||
<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.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.Forms" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
@@ -120,6 +122,7 @@
|
||||
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -131,6 +134,7 @@
|
||||
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,8 +4,8 @@ using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
@@ -17,7 +17,6 @@ namespace Bit.App
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
@@ -25,7 +24,6 @@ namespace Bit.App
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
|
||||
@@ -39,7 +37,6 @@ namespace Bit.App
|
||||
Current = this;
|
||||
return;
|
||||
}
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
@@ -47,7 +44,6 @@ namespace Bit.App
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
|
||||
@@ -84,8 +80,12 @@ namespace Bit.App
|
||||
}
|
||||
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 () =>
|
||||
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
|
||||
await LogOutAsync(userId, userInitiated, expired));
|
||||
}
|
||||
else if (message.Command == "loggedOut")
|
||||
{
|
||||
@@ -106,6 +106,18 @@ namespace Bit.App
|
||||
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")
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
@@ -167,7 +179,7 @@ namespace Bit.App
|
||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||
{
|
||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||
_storageService);
|
||||
_stateService);
|
||||
if (!updated)
|
||||
{
|
||||
SyncIfNeeded();
|
||||
@@ -191,7 +203,7 @@ namespace Bit.App
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
if (!isLocked)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
SetTabsPageFromAutofill(isLocked);
|
||||
await SleptAsync();
|
||||
@@ -236,27 +248,65 @@ namespace Bit.App
|
||||
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(() =>
|
||||
{
|
||||
Current.MainPage = new HomePage();
|
||||
if (expired)
|
||||
if (expired.GetValueOrDefault())
|
||||
{
|
||||
_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()
|
||||
{
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
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)
|
||||
{
|
||||
@@ -277,13 +327,13 @@ namespace Bit.App
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new HomePage(Options);
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
||||
@@ -326,12 +376,12 @@ namespace Bit.App
|
||||
{
|
||||
InitializeComponent();
|
||||
SetCulture();
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
Current.RequestedThemeChanged += (s, a) =>
|
||||
{
|
||||
UpdateTheme();
|
||||
};
|
||||
Current.MainPage = new HomePage();
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
var mainPageTask = SetMainPageAsync();
|
||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||
}
|
||||
@@ -357,17 +407,16 @@ namespace Bit.App
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LockedAsync(bool autoPromptBiometric)
|
||||
{
|
||||
await _stateService.PurgeAsync();
|
||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
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);
|
||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||
}
|
||||
|
||||
101
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
101
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal 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>
|
||||
35
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
35
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
31
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
114
src/App/Controls/AvatarImageSource.cs
Normal file
114
src/App/Controls/AvatarImageSource.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/App/Controls/ExtendedToolbarItem.cs
Normal file
9
src/App/Controls/ExtendedToolbarItem.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class ExtendedToolbarItem : ToolbarItem
|
||||
{
|
||||
public bool UseOriginalImage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Bit.App.Pages
|
||||
public class BaseChangePasswordViewModel : BaseViewModel
|
||||
{
|
||||
protected readonly IPlatformUtilsService _platformUtilsService;
|
||||
protected readonly IUserService _userService;
|
||||
protected readonly IStateService _stateService;
|
||||
protected readonly IPolicyService _policyService;
|
||||
protected readonly IPasswordGenerationService _passwordGenerationService;
|
||||
protected readonly II18nService _i18nService;
|
||||
@@ -31,7 +31,7 @@ namespace Bit.App.Pages
|
||||
protected BaseChangePasswordViewModel()
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
@@ -172,7 +172,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||
{
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
|
||||
@@ -10,14 +10,11 @@ namespace Bit.App.Pages
|
||||
public partial class EnvironmentPage : BaseContentPage
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly EnvironmentPageViewModel _vm;
|
||||
|
||||
public EnvironmentPage()
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as EnvironmentPageViewModel;
|
||||
_vm.Page = this;
|
||||
@@ -35,7 +32,6 @@ namespace Bit.App.Pages
|
||||
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,53 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.HomePage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:HomeViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
<ContentPage.BindingContext>
|
||||
<pages:HomeViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<StackLayout Spacing="0" Padding="10, 5">
|
||||
<controls:FaButton Text=""
|
||||
StyleClass="btn-muted, btn-icon, btn-icon-platform"
|
||||
HorizontalOptions="Start"
|
||||
Clicked="Environment_Clicked"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Options}">
|
||||
<controls:FaButton.Margin>
|
||||
<OnPlatform x:TypeArguments="Thickness">
|
||||
<On Platform="iOS" Value="0, 10, 0, 0" />
|
||||
<On Platform="Android" Value="0" />
|
||||
</OnPlatform>
|
||||
</controls:FaButton.Margin>
|
||||
</controls:FaButton>
|
||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center"></Label>
|
||||
<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" />
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
|
||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center">
|
||||
</Label>
|
||||
<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>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</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>
|
||||
|
||||
@@ -12,13 +12,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly HomeViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
private IMessagingService _messagingService;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
|
||||
public HomePage(AppOptions appOptions = null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
@@ -29,6 +26,11 @@ namespace Bit.App.Pages
|
||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||
UpdateLogo();
|
||||
|
||||
if (!_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_closeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DismissRegisterPageAndLogInAsync(string email)
|
||||
@@ -37,10 +39,18 @@ namespace Bit.App.Pages
|
||||
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
protected override async void 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) =>
|
||||
{
|
||||
if (message.Command == "updatedTheme")
|
||||
@@ -127,5 +137,23 @@ namespace Bit.App.Pages
|
||||
var page = new EnvironmentPage();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
using System;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class HomeViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
public HomeViewModel()
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
|
||||
PageTitle = AppResources.Bitwarden;
|
||||
}
|
||||
|
||||
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public Action StartLoginAction { get; set; }
|
||||
public Action StartRegisterAction { get; set; }
|
||||
public Action StartSsoLoginAction { get; set; }
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LockPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LockPageViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
@@ -13,6 +15,19 @@
|
||||
<pages:LockPageViewModel />
|
||||
</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>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
@@ -25,119 +40,162 @@
|
||||
x:Name="_logOut"
|
||||
Clicked="LogOut_Clicked"
|
||||
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>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
</ContentPage.ToolbarItems>
|
||||
<AbsoluteLayout
|
||||
x:Name="_absLayout"
|
||||
VerticalOptions="FillAndExpand"
|
||||
HorizontalOptions="FillAndExpand">
|
||||
<ContentView
|
||||
x:Name="_mainContent"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<ScrollView>
|
||||
<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>
|
||||
<!-- 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>
|
||||
<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>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
@@ -63,6 +61,15 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
_appeared = true;
|
||||
_mainContent.Content = _mainLayout;
|
||||
if (await ShowAccountSwitcherAsync())
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
await _vm.InitAsync();
|
||||
if (!_vm.BiometricLock)
|
||||
{
|
||||
@@ -147,5 +154,23 @@ namespace Bit.App.Pages
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
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;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -20,10 +20,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IBiometricService _biometricService;
|
||||
@@ -48,10 +45,7 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||
@@ -119,6 +113,11 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _lockedVerifyText, value);
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public Command SubmitCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||
@@ -129,7 +128,7 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_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();
|
||||
|
||||
// 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();
|
||||
}
|
||||
_email = await _userService.GetEmailAsync();
|
||||
_email = await _stateService.GetEmailAsync();
|
||||
var webVault = _environmentService.GetWebVaultUrl();
|
||||
if (string.IsNullOrWhiteSpace(webVault))
|
||||
{
|
||||
@@ -203,8 +202,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
ShowPassword = false;
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
|
||||
if (PinLock)
|
||||
{
|
||||
@@ -215,9 +214,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
||||
_vaultTimeoutService.PinProtectedKey);
|
||||
await _stateService.GetPinProtectedCachedAsync());
|
||||
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);
|
||||
failed = decPin != Pin;
|
||||
if (!failed)
|
||||
@@ -286,12 +285,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_pinSet.Item1)
|
||||
{
|
||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
||||
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;
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
@@ -357,7 +356,7 @@ namespace Bit.App.Pages
|
||||
page.MasterPasswordEntry.Focus();
|
||||
}
|
||||
});
|
||||
_vaultTimeoutService.BiometricLocked = !success;
|
||||
_stateService.BiometricLocked = !success;
|
||||
if (success)
|
||||
{
|
||||
await DoContinueAsync();
|
||||
@@ -376,9 +375,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task DoContinueAsync()
|
||||
{
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
_stateService.BiometricLocked = false;
|
||||
_messagingService.Send("unlocked");
|
||||
UnlockedAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LoginPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LoginPageViewModel"
|
||||
Title="{Binding PageTitle}">
|
||||
@@ -13,6 +15,21 @@
|
||||
<pages:LoginPageViewModel />
|
||||
</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>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
@@ -25,68 +42,109 @@
|
||||
x:Name="_getPasswordHint"
|
||||
Clicked="Hint_Clicked"
|
||||
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>
|
||||
</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>
|
||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||
</ContentPage.ToolbarItems>
|
||||
<!-- Account Switching Overlay -->
|
||||
<ContentView
|
||||
x:Name="_accountListOverlay"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
IsVisible="False"
|
||||
BackgroundColor="#22000000"
|
||||
Padding="0">
|
||||
|
||||
<ScrollView>
|
||||
<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
|
||||
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>
|
||||
<StackLayout Padding="10, 0">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</ContentView>
|
||||
</AbsoluteLayout>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
@@ -11,8 +9,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginPage : BaseContentPage
|
||||
{
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly LoginPageViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
@@ -20,9 +16,6 @@ namespace Bit.App.Pages
|
||||
|
||||
public LoginPage(string email = null, AppOptions appOptions = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as LoginPageViewModel;
|
||||
@@ -33,7 +26,6 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
_vm.Email = email;
|
||||
@@ -54,6 +46,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
ToolbarItems.Add(_getPasswordHint);
|
||||
}
|
||||
|
||||
if (!_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_closeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public Entry MasterPasswordEntry { get; set; }
|
||||
@@ -61,6 +58,15 @@ namespace Bit.App.Pages
|
||||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
_mainContent.Content = _mainLayout;
|
||||
if (await ShowAccountSwitcherAsync())
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
await _vm.InitAsync();
|
||||
if (!_inputFocused)
|
||||
{
|
||||
@@ -130,5 +136,23 @@ namespace Bit.App.Pages
|
||||
var page = new UpdateTempPasswordPage();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Models.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginPageViewModel : CaptchaProtectedViewModel
|
||||
{
|
||||
private const string Keys_RememberedEmail = "rememberedEmail";
|
||||
private const string Keys_RememberEmail = "rememberEmail";
|
||||
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
@@ -34,7 +30,6 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
@@ -67,6 +62,11 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _masterPassword, value);
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public Command LogInCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||
@@ -85,9 +85,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -131,11 +131,11 @@ namespace Bit.App.Pages
|
||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||
if (RememberEmail)
|
||||
{
|
||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
||||
await _stateService.SetRememberedEmailAsync(Email);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
||||
await _stateService.SetRememberedEmailAsync(null);
|
||||
}
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
@@ -163,8 +163,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
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));
|
||||
LogInSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class LoginSsoPage : BaseContentPage
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly LoginSsoPageViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
@@ -20,10 +18,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public LoginSsoPage(AppOptions appOptions = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as LoginSsoPageViewModel;
|
||||
@@ -36,7 +31,6 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
@@ -16,16 +15,12 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class LoginSsoPageViewModel : BaseViewModel
|
||||
{
|
||||
private const string Keys_RememberedOrgIdentifier = "rememberedOrgIdentifier";
|
||||
private const string Keys_RememberOrgIdentifier = "rememberOrgIdentifier";
|
||||
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
@@ -40,7 +35,6 @@ namespace Bit.App.Pages
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
|
||||
@@ -66,9 +60,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -172,11 +166,11 @@ namespace Bit.App.Pages
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
if (RememberOrgIdentifier)
|
||||
{
|
||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier);
|
||||
await _stateService.SetRememberedOrgIdentifierAsync(null);
|
||||
}
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if (response.TwoFactor)
|
||||
@@ -193,8 +187,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
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));
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -8,22 +6,18 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class RegisterPage : BaseContentPage
|
||||
{
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly RegisterPageViewModel _vm;
|
||||
|
||||
private bool _inputFocused;
|
||||
|
||||
public RegisterPage(HomePage homePage)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as RegisterPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
MasterPasswordEntry = _masterPassword;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
@@ -10,14 +8,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class SetPasswordPage : BaseContentPage
|
||||
{
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly SetPasswordPageViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as SetPasswordPageViewModel;
|
||||
@@ -26,7 +21,6 @@ namespace Bit.App.Pages
|
||||
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
_messagingService.Send("showStatusBar", false);
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
_vm.OrgIdentifier = orgIdentifier;
|
||||
|
||||
@@ -7,7 +7,6 @@ using Bit.Core.Models.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
||||
private readonly IApiService _apiService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly II18nService _i18nService;
|
||||
@@ -40,7 +39,7 @@ namespace Bit.App.Pages
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
@@ -159,7 +158,7 @@ namespace Bit.App.Pages
|
||||
|
||||
var kdf = KdfType.PBKDF2_SHA256;
|
||||
var kdfIterations = 100000;
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||
@@ -196,8 +195,8 @@ namespace Bit.App.Pages
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||
// Set Password and relevant information
|
||||
await _apiService.SetPasswordAsync(request);
|
||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
||||
await _stateService.SetKdfTypeAsync(kdf);
|
||||
await _stateService.SetKdfIterationsAsync(kdfIterations);
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
@@ -216,7 +215,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
ResetPasswordKey = encryptedKey.EncryptedString
|
||||
};
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
// Enroll user
|
||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||
}
|
||||
@@ -289,7 +288,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||
{
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
List<string> userInput = null;
|
||||
var atPosition = email.IndexOf('@');
|
||||
if (atPosition > -1)
|
||||
|
||||
@@ -14,8 +14,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
private TwoFactorPageViewModel _vm;
|
||||
@@ -30,10 +28,8 @@ namespace Bit.App.Pages
|
||||
_authingWithSso = authingWithSso ?? false;
|
||||
_appOptions = appOptions;
|
||||
_orgIdentifier = orgIdentifier;
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_vm = BindingContext as TwoFactorPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.StartSetPasswordAction = () =>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@@ -23,7 +22,6 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
@@ -43,7 +41,6 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
@@ -303,8 +300,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
||||
TwoFactorAuthSuccessAction?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ namespace Bit.App.Pages
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
|
||||
// Service Use
|
||||
_messagingService.Send("showStatusBar", true);
|
||||
|
||||
// Binding
|
||||
InitializeComponent();
|
||||
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||
|
||||
@@ -43,9 +43,9 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// Retrieve details for key generation
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
|
||||
// Create new key and hash new password
|
||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Enums;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.PlatformConfiguration;
|
||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||
@@ -13,8 +14,9 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class BaseContentPage : ContentPage
|
||||
{
|
||||
private IStorageService _storageService;
|
||||
private IStateService _stateService;
|
||||
private IDeviceActionService _deviceActionService;
|
||||
private IMessagingService _messagingService;
|
||||
|
||||
protected int ShowModalAnimationDelay = 400;
|
||||
protected int ShowPageAnimationDelay = 100;
|
||||
@@ -30,10 +32,10 @@ namespace Bit.App.Pages
|
||||
|
||||
public DateTime? LastPageAction { get; set; }
|
||||
|
||||
protected override void OnAppearing()
|
||||
protected async override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
SaveActivity();
|
||||
await SaveActivity();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (_storageService == null)
|
||||
if (_stateService == null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
}
|
||||
if (_deviceActionService == null)
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
}
|
||||
if (_messagingService == null)
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveActivity()
|
||||
private async Task SaveActivity()
|
||||
{
|
||||
SetServices();
|
||||
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -6,6 +7,7 @@ namespace Bit.App.Pages
|
||||
public abstract class BaseViewModel : ExtendedViewModel
|
||||
{
|
||||
private string _pageTitle = string.Empty;
|
||||
private AvatarImageSource _avatar;
|
||||
|
||||
public string PageTitle
|
||||
{
|
||||
@@ -13,6 +15,12 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _pageTitle, value);
|
||||
}
|
||||
|
||||
public AvatarImageSource AvatarImageSource
|
||||
{
|
||||
get => _avatar ?? new AvatarImageSource();
|
||||
set => SetProperty(ref _avatar, value);
|
||||
}
|
||||
|
||||
public ContentPage Page { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ISendService _sendService;
|
||||
private bool _sendEnabled;
|
||||
private bool _canAccessPremium;
|
||||
@@ -51,7 +51,7 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
|
||||
@@ -220,8 +220,8 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||
_canAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _userService.GetEmailVerifiedAsync();
|
||||
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_emailVerified = await _stateService.GetEmailVerifiedAsync();
|
||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||
|
||||
@@ -32,21 +32,19 @@ namespace Bit.App.Pages
|
||||
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStorageService _storageService;
|
||||
|
||||
public SendGroupingsPageViewModel()
|
||||
{
|
||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
|
||||
Loading = true;
|
||||
PageTitle = AppResources.Send;
|
||||
@@ -116,7 +114,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.FullSyncAsync(false);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -11,7 +10,7 @@ namespace Bit.App.Pages
|
||||
public class AutofillServicesPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly MobileI18nService _i18nService;
|
||||
|
||||
private bool _autofillServiceToggled;
|
||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
||||
public AutofillServicesPageViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||
PageTitle = AppResources.AutofillServices;
|
||||
}
|
||||
@@ -152,7 +151,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
@@ -202,7 +201,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled);
|
||||
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class ExtensionPageViewModel : BaseViewModel
|
||||
{
|
||||
private const string StartedKey = "appExtensionStarted";
|
||||
private const string ActivatedKey = "appExtensionActivated";
|
||||
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
private bool _started;
|
||||
private bool _activated;
|
||||
@@ -20,8 +16,7 @@ namespace Bit.App.Pages
|
||||
public ExtensionPageViewModel()
|
||||
{
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
PageTitle = AppResources.AppExtension;
|
||||
}
|
||||
|
||||
@@ -52,8 +47,8 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
var started = await _storageService.GetAsync<bool?>(StartedKey);
|
||||
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
|
||||
var started = await _stateService.GetAppExtensionStartedAsync();
|
||||
var activated = await _stateService.GetAppExtensionActivatedAsync();
|
||||
Started = started.GetValueOrDefault();
|
||||
Activated = activated.GetValueOrDefault();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
@@ -13,9 +12,6 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public class OptionsPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ITotpService _totpService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -34,9 +30,6 @@ namespace Bit.App.Pages
|
||||
|
||||
public OptionsPageViewModel()
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
@@ -166,19 +159,17 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
AutofillDisableSavePrompt = (await _storageService.GetAsync<bool?>(
|
||||
Constants.AutofillDisableSavePromptKey)).GetValueOrDefault();
|
||||
var blacklistedUrisList = await _storageService.GetAsync<List<string>>(
|
||||
Constants.AutofillBlacklistedUrisKey);
|
||||
AutofillDisableSavePrompt = (await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
|
||||
var blacklistedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
||||
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
||||
DisableFavicon = (await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey)).GetValueOrDefault();
|
||||
var theme = await _storageService.GetAsync<string>(Constants.ThemeKey);
|
||||
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
var theme = await _stateService.GetThemeAsync();
|
||||
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
||||
var defaultUriMatch = await _storageService.GetAsync<int?>(Constants.DefaultUriMatch);
|
||||
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
|
||||
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
||||
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);
|
||||
_inited = true;
|
||||
}
|
||||
@@ -187,7 +178,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy);
|
||||
await _stateService.SetDisableAutoTotpCopyAsync(DisableAutoTotpCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +186,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
||||
await _stateService.SetDisableFaviconAsync(DisableFavicon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,8 +194,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited && ClearClipboardSelectedIndex > -1)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.ClearClipboardKey,
|
||||
ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||
await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,8 +203,8 @@ namespace Bit.App.Pages
|
||||
if (_inited && ThemeSelectedIndex > -1)
|
||||
{
|
||||
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
||||
await _storageService.SaveAsync(Constants.ThemeKey, theme);
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Application.Current.Resources);
|
||||
await _stateService.SetThemeAsync(theme);
|
||||
ThemeManager.SetTheme(Application.Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
}
|
||||
}
|
||||
@@ -224,8 +213,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited && UriMatchSelectedIndex > -1)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.DefaultUriMatch,
|
||||
(int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||
await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +221,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt);
|
||||
await _stateService.SetAutofillDisableSavePromptAsync(AutofillDisableSavePrompt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +231,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
||||
{
|
||||
await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey);
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(null);
|
||||
AutofillBlacklistedUris = null;
|
||||
return;
|
||||
}
|
||||
@@ -265,7 +253,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
urisList.Add(cleanedUri);
|
||||
}
|
||||
await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList);
|
||||
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
|
||||
AutofillBlacklistedUris = string.Join(", ", urisList);
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
@@ -17,12 +16,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IEnvironmentService _environmentService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IBiometricService _biometricService;
|
||||
private readonly IPolicyService _policyService;
|
||||
@@ -68,12 +66,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||
@@ -111,7 +108,7 @@ namespace Bit.App.Pages
|
||||
|
||||
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
||||
_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;
|
||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||
_pin = pinSet.Item1 || pinSet.Item2;
|
||||
@@ -151,7 +148,7 @@ namespace Bit.App.Pages
|
||||
List<string> fingerprint;
|
||||
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.")
|
||||
{
|
||||
@@ -329,9 +326,9 @@ namespace Bit.App.Pages
|
||||
AppResources.Yes, AppResources.No);
|
||||
}
|
||||
|
||||
var kdf = await _userService.GetKdfAsync();
|
||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||
var email = await _userService.GetEmailAsync();
|
||||
var kdf = await _stateService.GetKdfTypeAsync();
|
||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
||||
kdfIterations.GetValueOrDefault(5000));
|
||||
@@ -341,12 +338,12 @@ namespace Bit.App.Pages
|
||||
if (masterPassOnRestart)
|
||||
{
|
||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
|
||||
_vaultTimeoutService.PinProtectedKey = pinProtectedKey;
|
||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||
await _stateService.SetPinProtectedCachedAsync(pinProtectedKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
|
||||
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -381,13 +378,13 @@ namespace Bit.App.Pages
|
||||
if (_biometric)
|
||||
{
|
||||
await _biometricService.SetupBiometricAsync();
|
||||
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true);
|
||||
await _stateService.SetBiometricUnlockAsync(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storageService.RemoveAsync(Constants.BiometricUnlockKey);
|
||||
await _stateService.SetBiometricUnlockAsync(null);
|
||||
}
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
_stateService.BiometricLocked = false;
|
||||
await _cryptoService.ToggleKeyAsync();
|
||||
BuildList();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -12,7 +11,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly ILocalizeService _localizeService;
|
||||
|
||||
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||
|
||||
@@ -52,7 +51,7 @@ namespace Bit.App.Pages
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await SetLastSyncAsync();
|
||||
EnableSyncOnRefresh = await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey);
|
||||
EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (_inited)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.SyncOnRefreshKey, _syncOnRefresh);
|
||||
await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -45,7 +46,7 @@ namespace Bit.App.Pages
|
||||
var settingsPage = new NavigationPage(new SettingsPage(this))
|
||||
{
|
||||
Title = AppResources.Settings,
|
||||
IconImageSource = "cog.png"
|
||||
IconImageSource = "cog_settings.png"
|
||||
};
|
||||
Children.Add(settingsPage);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
||||
public partial class AddEditPage : BaseContentPage
|
||||
{
|
||||
private readonly AppOptions _appOptions;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
@@ -38,7 +37,7 @@ namespace Bit.App.Pages
|
||||
bool cloneMode = false,
|
||||
ViewPage viewPage = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||
@@ -333,10 +332,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var addLoginShown = await _storageService.GetAsync<bool?>(Constants.AddSitePromptShownKey);
|
||||
var addLoginShown = await _stateService.GetAddSitePromptShownAsync();
|
||||
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 (_deviceActionService.SystemMajorVersion() < 12)
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace Bit.App.Pages
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
@@ -70,7 +71,8 @@ namespace Bit.App.Pages
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_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");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
@@ -302,9 +304,9 @@ namespace Bit.App.Pages
|
||||
|
||||
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));
|
||||
var orgs = await _userService.GetAllOrganizationAsync();
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
foreach (var org in orgs.OrderBy(o => o.Name))
|
||||
{
|
||||
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IVaultTimeoutService _timeoutService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private CipherView _cipher;
|
||||
private Cipher _cipherDomain;
|
||||
@@ -33,8 +33,8 @@ namespace Bit.App.Pages
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_timeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
||||
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
||||
PageTitle = AppResources.Attachments;
|
||||
@@ -66,7 +66,7 @@ namespace Bit.App.Pages
|
||||
Cipher = await _cipherDomain.DecryptAsync();
|
||||
LoadAttachments();
|
||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
||||
var canAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||
if (!_canAccessAttachments)
|
||||
{
|
||||
@@ -140,7 +140,7 @@ namespace Bit.App.Pages
|
||||
// Prevent Android from locking if vault timeout set to "immediate"
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
_timeoutService.DelayLockAndLogoutMs = 60000;
|
||||
_vaultTimeoutService.DelayLockAndLogoutMs = 60000;
|
||||
}
|
||||
await _deviceActionService.SelectFileAsync();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@@ -88,8 +86,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
ShowList = false;
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
||||
{
|
||||
Search((Page as CiphersPage).SearchBar.Text, 200);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<pages:BaseContentPage 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.Pages.GroupingsPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||
x:DataType="pages:GroupingsPageViewModel"
|
||||
Title="{Binding PageTitle}"
|
||||
x:Name="_page">
|
||||
@@ -15,6 +17,15 @@
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<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"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Search}" />
|
||||
@@ -135,6 +146,8 @@
|
||||
AbsoluteLayout.LayoutFlags="All"
|
||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||
</ContentView>
|
||||
|
||||
<!-- Android FAB -->
|
||||
<Button
|
||||
x:Name="_fab"
|
||||
Image="plus.png"
|
||||
@@ -148,6 +161,40 @@
|
||||
<effects:FabShadowEffect />
|
||||
</Button.Effects>
|
||||
</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>
|
||||
|
||||
</pages:BaseContentPage>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -9,6 +7,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Models.Data;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
@@ -37,7 +36,7 @@ namespace Bit.App.Pages
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
@@ -70,6 +69,10 @@ namespace Bit.App.Pages
|
||||
_absLayout.Children.Remove(_fab);
|
||||
ToolbarItems.Remove(_addItem);
|
||||
}
|
||||
if (!mainPage)
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
protected async override void OnAppearing()
|
||||
@@ -80,6 +83,11 @@ namespace Bit.App.Pages
|
||||
IsBusy = true;
|
||||
}
|
||||
|
||||
if (_vm.MainPage)
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
|
||||
_broadcasterService.Subscribe(_pageName, async (message) =>
|
||||
{
|
||||
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 () =>
|
||||
{
|
||||
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
||||
@@ -128,10 +136,10 @@ namespace Bit.App.Pages
|
||||
!_vm.HasCiphers &&
|
||||
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
|
||||
{
|
||||
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync);
|
||||
var triedV1ReSync = await _stateService.GetTriedV1ResyncAsync();
|
||||
if (!triedV1ReSync.GetValueOrDefault())
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.TriedV1Resync, true);
|
||||
await _stateService.SetTriedV1ResyncAsync(true);
|
||||
await _syncService.FullSyncAsync(true);
|
||||
}
|
||||
}
|
||||
@@ -145,14 +153,14 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
// Push registration
|
||||
var lastPushRegistration = await _storageService.GetAsync<DateTime?>(Constants.PushLastRegistrationDateKey);
|
||||
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
|
||||
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var pushPromptShow = await _storageService.GetAsync<bool?>(Constants.PushInitialPromptShownKey);
|
||||
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
|
||||
if (!pushPromptShow.GetValueOrDefault(false))
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PushInitialPromptShownKey, true);
|
||||
await _stateService.SetPushInitialPromptShownAsync(true);
|
||||
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
||||
AppResources.OkGotIt);
|
||||
}
|
||||
@@ -173,8 +181,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (migratedFromV1.GetValueOrDefault())
|
||||
{
|
||||
var migratedFromV1AutofillPromptShown = await _storageService.GetAsync<bool?>(
|
||||
Constants.MigratedFromV1AutofillPromptShown);
|
||||
var migratedFromV1AutofillPromptShown =
|
||||
await _stateService.GetMigratedFromV1AutofillPromptShownAsync();
|
||||
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
|
||||
{
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
@@ -11,6 +10,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -39,13 +39,11 @@ namespace Bit.App.Pages
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||
|
||||
public GroupingsPageViewModel()
|
||||
@@ -54,13 +52,11 @@ namespace Bit.App.Pages
|
||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
|
||||
Loading = true;
|
||||
@@ -80,7 +76,6 @@ namespace Bit.App.Pages
|
||||
public string CollectionId { get; set; }
|
||||
public Func<CipherView, bool> Filter { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
public bool HasCiphers { get; set; }
|
||||
public bool HasFolders { get; set; }
|
||||
public bool HasCollections { get; set; }
|
||||
@@ -139,6 +134,11 @@ namespace Bit.App.Pages
|
||||
get => _websiteIconsEnabled;
|
||||
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||
}
|
||||
public ExtendedObservableCollection<AccountView> AccountViews
|
||||
{
|
||||
get => _stateService.AccountViews;
|
||||
}
|
||||
|
||||
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
|
||||
public Command RefreshCommand { get; set; }
|
||||
public Command<CipherView> CipherOptionsCommand { get; set; }
|
||||
@@ -150,7 +150,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
@@ -159,7 +159,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.FullSyncAsync(false);
|
||||
@@ -175,8 +175,7 @@ namespace Bit.App.Pages
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var page = Page as GroupingsPage;
|
||||
|
||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
||||
.GetValueOrDefault();
|
||||
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||
try
|
||||
{
|
||||
await LoadDataAsync();
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private CipherView _cipher;
|
||||
private int _organizationSelectedIndex;
|
||||
@@ -28,7 +28,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
||||
@@ -67,7 +67,7 @@ namespace Bit.App.Pages
|
||||
var allCollections = await _collectionService.GetAllDecryptedAsync();
|
||||
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
|
||||
|
||||
var orgs = await _userService.GetAllOrganizationAsync();
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
OrganizationOptions = orgs.OrderBy(o => o.Name)
|
||||
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
|
||||
.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 _deviceActionService.HideLoadingAsync();
|
||||
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
||||
(await _userService.GetOrganizationAsync(OrganizationId)).Name);
|
||||
(await _organizationService.GetAsync(OrganizationId)).Name);
|
||||
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
return true;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ITotpService _totpService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuditService _auditService;
|
||||
@@ -48,7 +48,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||
@@ -248,7 +248,7 @@ namespace Bit.App.Pages
|
||||
return false;
|
||||
}
|
||||
Cipher = await cipher.DecryptAsync();
|
||||
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||
CanAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
||||
|
||||
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||
|
||||
@@ -13,15 +13,10 @@ namespace Bit.App.Services
|
||||
|
||||
private readonly HashSet<string> _preferenceStorageKeys = new HashSet<string>
|
||||
{
|
||||
Constants.VaultTimeoutKey,
|
||||
Constants.VaultTimeoutActionKey,
|
||||
Constants.ThemeKey,
|
||||
Constants.DefaultUriMatch,
|
||||
Constants.DisableAutoTotpCopyKey,
|
||||
Constants.DisableFaviconKey,
|
||||
Constants.ClearClipboardKey,
|
||||
Constants.AutofillDisableSavePromptKey,
|
||||
Constants.LastActiveTimeKey,
|
||||
Constants.AppIdKey,
|
||||
Constants.PreAuthEnvironmentUrlsKey,
|
||||
Constants.AutofillTileAdded,
|
||||
Constants.AddSitePromptShownKey,
|
||||
Constants.PushInitialPromptShownKey,
|
||||
Constants.LastFileCacheClearKey,
|
||||
Constants.PushLastRegistrationDateKey,
|
||||
@@ -37,14 +32,17 @@ namespace Bit.App.Services
|
||||
Constants.iOSAutoFillBiometricIntegrityKey,
|
||||
Constants.iOSExtensionClearCiphersCacheKey,
|
||||
Constants.iOSExtensionBiometricIntegrityKey,
|
||||
Constants.EnvironmentUrlsKey,
|
||||
Constants.InlineAutofillEnabledKey,
|
||||
Constants.InvalidUnlockAttempts,
|
||||
Constants.RememberEmailKey,
|
||||
Constants.RememberedEmailKey,
|
||||
Constants.RememberOrgIdentifierKey,
|
||||
Constants.RememberedOrgIdentifierKey,
|
||||
Constants.AppExtensionStartedKey,
|
||||
Constants.AppExtensionActivatedKey,
|
||||
};
|
||||
|
||||
private readonly HashSet<string> _migrateToPreferences = new HashSet<string>
|
||||
{
|
||||
Constants.EnvironmentUrlsKey,
|
||||
"environmentUrls",
|
||||
};
|
||||
private readonly HashSet<string> _haveMigratedToPreferences = new HashSet<string>();
|
||||
|
||||
|
||||
@@ -19,9 +19,8 @@ namespace Bit.App.Services
|
||||
{
|
||||
private bool _showNotification;
|
||||
private bool _resolved;
|
||||
private IStorageService _storageService;
|
||||
private ISyncService _syncService;
|
||||
private IUserService _userService;
|
||||
private IStateService _stateService;
|
||||
private IAppIdService _appIdService;
|
||||
private IApiService _apiService;
|
||||
private IMessagingService _messagingService;
|
||||
@@ -58,8 +57,8 @@ namespace Bit.App.Services
|
||||
return;
|
||||
}
|
||||
|
||||
var myUserId = await _userService.GetUserIdAsync();
|
||||
var isAuthenticated = await _userService.IsAuthenticatedAsync();
|
||||
var myUserId = await _stateService.GetActiveUserIdAsync();
|
||||
var isAuthenticated = await _stateService.IsAuthenticatedAsync();
|
||||
switch (notification.Type)
|
||||
{
|
||||
case NotificationType.SyncCipherUpdate:
|
||||
@@ -129,7 +128,7 @@ namespace Bit.App.Services
|
||||
{
|
||||
Resolve();
|
||||
Debug.WriteLine(string.Format("Push Notification - Device Registered - Token : {0}", token));
|
||||
var isAuthenticated = await _userService.IsAuthenticatedAsync();
|
||||
var isAuthenticated = await _stateService.IsAuthenticatedAsync();
|
||||
if (!isAuthenticated)
|
||||
{
|
||||
return;
|
||||
@@ -141,10 +140,10 @@ namespace Bit.App.Services
|
||||
await _apiService.PutDeviceTokenAsync(appId,
|
||||
new Core.Models.Request.DeviceTokenRequest { PushToken = token });
|
||||
Debug.WriteLine("Registered device with server.");
|
||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
||||
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||
if (deviceType == Device.Android)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.PushCurrentTokenKey, token);
|
||||
await _stateService.SetPushCurrentTokenAsync(token);
|
||||
}
|
||||
}
|
||||
catch (ApiException)
|
||||
@@ -174,9 +173,8 @@ namespace Bit.App.Services
|
||||
{
|
||||
return;
|
||||
}
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -14,6 +13,7 @@ using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
@@ -46,8 +46,8 @@ namespace Bit.App.Utilities
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(cipher.Login.Totp))
|
||||
{
|
||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var canAccessPremium = await stateService.CanAccessPremiumAsync();
|
||||
if (canAccessPremium || cipher.OrganizationUseTotp)
|
||||
{
|
||||
options.Add(AppResources.CopyTotp);
|
||||
@@ -330,33 +330,15 @@ namespace Bit.App.Utilities
|
||||
}
|
||||
|
||||
public static async Task<bool> PerformUpdateTasksAsync(ISyncService syncService,
|
||||
IDeviceActionService deviceActionService, IStorageService storageService)
|
||||
IDeviceActionService deviceActionService, IStateService stateService)
|
||||
{
|
||||
var currentBuild = deviceActionService.GetBuildNumber();
|
||||
var lastBuild = await storageService.GetAsync<string>(Constants.LastBuildKey);
|
||||
if (lastBuild == null)
|
||||
{
|
||||
// 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)
|
||||
var lastBuild = await stateService.GetLastBuildAsync();
|
||||
if (lastBuild == null || lastBuild != currentBuild)
|
||||
{
|
||||
// Updated
|
||||
var tasks = Task.Run(() => syncService.FullSyncAsync(true));
|
||||
}
|
||||
if (lastBuild != currentBuild)
|
||||
{
|
||||
await storageService.SaveAsync(Constants.LastBuildKey, currentBuild);
|
||||
await stateService.SetLastBuildAsync(currentBuild);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -418,35 +400,34 @@ namespace Bit.App.Utilities
|
||||
|
||||
public static async Task<PreviousPageInfo> ClearPreviousPage()
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
var previousPage = await storageService.GetAsync<PreviousPageInfo>(Constants.PreviousPageKey);
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var previousPage = await stateService.GetPreviousPageInfoAsync();
|
||||
if (previousPage != null)
|
||||
{
|
||||
await storageService.RemoveAsync(Constants.PreviousPageKey);
|
||||
await stateService.SetPreviousPageInfoAsync(null);
|
||||
}
|
||||
return previousPage;
|
||||
}
|
||||
|
||||
public static async Task<int> IncrementInvalidUnlockAttemptsAsync()
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
var invalidUnlockAttempts = await storageService.GetAsync<int>(Constants.InvalidUnlockAttempts);
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var invalidUnlockAttempts = await stateService.GetInvalidUnlockAttemptsAsync();
|
||||
invalidUnlockAttempts++;
|
||||
await storageService.SaveAsync(Constants.InvalidUnlockAttempts, invalidUnlockAttempts);
|
||||
await stateService.SetInvalidUnlockAttemptsAsync(invalidUnlockAttempts);
|
||||
return invalidUnlockAttempts;
|
||||
}
|
||||
|
||||
public static async Task ResetInvalidUnlockAttemptsAsync()
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
await storageService.RemoveAsync(Constants.InvalidUnlockAttempts);
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
await stateService.SetInvalidUnlockAttemptsAsync(null);
|
||||
}
|
||||
|
||||
public static async Task<bool> IsVaultTimeoutImmediateAsync()
|
||||
{
|
||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
|
||||
var vaultTimeoutMinutes = await storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
var vaultTimeoutMinutes = await stateService.GetVaultTimeoutAsync();
|
||||
if (vaultTimeoutMinutes.GetValueOrDefault(-1) == 0)
|
||||
{
|
||||
return true;
|
||||
@@ -466,9 +447,8 @@ namespace Bit.App.Utilities
|
||||
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 tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
|
||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||
@@ -483,21 +463,61 @@ namespace Bit.App.Utilities
|
||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
var searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
||||
|
||||
var userId = await userService.GetUserIdAsync();
|
||||
if (userId == null)
|
||||
{
|
||||
userId = await stateService.GetActiveUserIdAsync();
|
||||
}
|
||||
|
||||
await Task.WhenAll(
|
||||
syncService.SetLastSyncAsync(DateTime.MinValue),
|
||||
tokenService.ClearTokenAsync(),
|
||||
cryptoService.ClearKeysAsync(),
|
||||
userService.ClearAsync(),
|
||||
settingsService.ClearAsync(userId),
|
||||
cipherService.ClearAsync(userId),
|
||||
folderService.ClearAsync(userId),
|
||||
collectionService.ClearAsync(userId),
|
||||
passwordGenerationService.ClearAsync(),
|
||||
vaultTimeoutService.ClearAsync(),
|
||||
stateService.PurgeAsync(),
|
||||
passwordGenerationService.ClearAsync(userId),
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Styles;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
#if !FDROID
|
||||
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(
|
||||
string.Format(PreferencesStorageService.KeyFormat, Constants.ThemeKey), default(string),
|
||||
!android ? "group.com.8bit.bitwarden" : default(string));
|
||||
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
return stateService.GetThemeAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public static bool OsDarkModeEnabled()
|
||||
|
||||
@@ -5,6 +5,5 @@ namespace Bit.Core.Abstractions
|
||||
public interface IAppIdService
|
||||
{
|
||||
Task<string> GetAppIdAsync();
|
||||
Task<string> GetAnonymousAppIdAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ namespace Bit.Core.Abstractions
|
||||
Task UpsertAsync(CollectionData collection);
|
||||
Task UpsertAsync(List<CollectionData> collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface ICryptoService
|
||||
{
|
||||
Task ClearEncKeyAsync(bool memoryOnly = false);
|
||||
Task ClearKeyAsync();
|
||||
Task ClearKeyHashAsync();
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false);
|
||||
Task ClearKeysAsync();
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false);
|
||||
Task ClearPinProtectedKeyAsync();
|
||||
Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeyAsync(string userId = null);
|
||||
Task ClearKeyHashAsync(string userId = null);
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeysAsync(string userId = null);
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearPinProtectedKeyAsync(string userId = null);
|
||||
void ClearCache();
|
||||
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
|
||||
Task<byte[]> DecryptToBytesAsync(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<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
|
||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||
Task<SymmetricCryptoKey> GetKeyAsync();
|
||||
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
|
||||
Task<string> GetKeyHashAsync();
|
||||
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
|
||||
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
|
||||
@@ -33,7 +34,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<bool> CompareAndUpdateKeyHashAsync(string masterPassword, SymmetricCryptoKey key);
|
||||
Task<bool> HasEncKeyAsync();
|
||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||
Task<bool> HasKeyAsync();
|
||||
Task<bool> HasKeyAsync(string userId = null);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations);
|
||||
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations,
|
||||
|
||||
@@ -9,4 +9,4 @@ namespace Bit.Core.Abstractions
|
||||
Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false);
|
||||
Task UploadEventsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,4 @@ namespace Bit.Core.Abstractions
|
||||
Task UpsertAsync(FolderData folder);
|
||||
Task UpsertAsync(List<FolderData> folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/Core/Abstractions/IOrganizationService.cs
Normal file
16
src/Core/Abstractions/IOrganizationService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@ namespace Bit.Core.Abstractions
|
||||
public interface IPasswordGenerationService
|
||||
{
|
||||
Task AddHistoryAsync(string password, CancellationToken token = default(CancellationToken));
|
||||
Task ClearAsync();
|
||||
Task ClearAsync(string userId = null);
|
||||
void ClearCache();
|
||||
Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options);
|
||||
Task<string> GeneratePasswordAsync(PasswordGenerationOptions options);
|
||||
Task<List<GeneratedPasswordHistory>> GetHistoryAsync();
|
||||
|
||||
@@ -10,4 +10,4 @@ namespace Bit.Core.Abstractions
|
||||
Task<List<List<string>>> GetEquivalentDomainsAsync();
|
||||
Task SetEquivalentDomainsAsync(List<List<string>> equivalentDomains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
public interface IStateService
|
||||
{
|
||||
Task<T> GetAsync<T>(string key);
|
||||
Task RemoveAsync(string key);
|
||||
Task SaveAsync<T>(string key, T obj);
|
||||
Task PurgeAsync();
|
||||
bool BiometricLocked { get; set; }
|
||||
ExtendedObservableCollection<AccountView> AccountViews { get; set; }
|
||||
Task<List<string>> GetUserIdsAsync();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface ITokenService
|
||||
{
|
||||
Task ClearTokenAsync();
|
||||
Task ClearTokenAsync(string userId = null);
|
||||
Task ClearTwoFactorTokenAsync(string email);
|
||||
void ClearCache();
|
||||
JObject DecodeToken();
|
||||
string GetEmail();
|
||||
bool GetEmailVerified();
|
||||
string GetIssuer();
|
||||
string GetName();
|
||||
bool GetPremium();
|
||||
bool GetIsExternal();
|
||||
Task<bool> GetIsExternal();
|
||||
Task<string> GetRefreshTokenAsync();
|
||||
Task<string> GetTokenAsync();
|
||||
Task ToggleTokensAsync();
|
||||
@@ -22,7 +23,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetTwoFactorTokenAsync(string email);
|
||||
string GetUserId();
|
||||
Task SetRefreshTokenAsync(string refreshToken);
|
||||
Task SetTokenAsync(string token);
|
||||
Task SetAccessTokenAsync(string token);
|
||||
Task SetTokensAsync(string accessToken, string refreshToken);
|
||||
Task SetTwoFactorTokenAsync(string token, string email);
|
||||
bool TokenNeedsRefresh(int minutes = 5);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Domain;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IVaultTimeoutService
|
||||
{
|
||||
EncString PinProtectedKey { get; set; }
|
||||
bool BiometricLocked { get; set; }
|
||||
long? DelayLockAndLogoutMs { get; set; }
|
||||
|
||||
Task CheckVaultTimeoutAsync();
|
||||
Task ClearAsync();
|
||||
Task<bool> IsLockedAsync();
|
||||
Task<Tuple<bool, bool>> IsPinLockSetAsync();
|
||||
Task<bool> IsBiometricLockSetAsync();
|
||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false);
|
||||
Task LogOutAsync();
|
||||
Task<bool> ShouldTimeoutAsync(string userId = null);
|
||||
Task ExecuteTimeoutActionAsync(string userId = null);
|
||||
Task ClearAsync(string userId = null);
|
||||
Task<bool> IsLockedAsync(string userId = null);
|
||||
Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
|
||||
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
||||
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
||||
Task SetVaultTimeoutOptionsAsync(int? timeout, string action);
|
||||
Task<int?> GetVaultTimeout();
|
||||
Task<int?> GetVaultTimeout(string userId = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,31 +2,19 @@
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const int MaxAccounts = 5;
|
||||
public const string AndroidAppProtocol = "androidapp://";
|
||||
public const string iOSAppProtocol = "iosapp://";
|
||||
public static string SyncOnRefreshKey = "syncOnRefresh";
|
||||
public static string VaultTimeoutKey = "lockOption";
|
||||
public static string VaultTimeoutActionKey = "vaultTimeoutAction";
|
||||
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 StateKey = "state";
|
||||
public static string AppIdKey = "appId";
|
||||
public static string PreAuthEnvironmentUrlsKey = "preAuthEnvironmentUrls";
|
||||
public static string LastFileCacheClearKey = "lastFileCacheClear";
|
||||
public static string AutofillDisableSavePromptKey = "autofillDisableSavePrompt";
|
||||
public static string AutofillBlacklistedUrisKey = "autofillBlacklistedUris";
|
||||
public static string AutofillTileAdded = "autofillTileAdded";
|
||||
public static string DisableFaviconKey = "disableFavicon";
|
||||
public static string PushRegisteredTokenKey = "pushRegisteredToken";
|
||||
public static string PushCurrentTokenKey = "pushCurrentToken";
|
||||
public static string PushLastRegistrationDateKey = "pushLastRegistrationDate";
|
||||
public static string PushInitialPromptShownKey = "pushInitialPromptShown";
|
||||
public static string ThemeKey = "theme";
|
||||
public static string ClearClipboardKey = "clearClipboard";
|
||||
public static string LastBuildKey = "lastBuild";
|
||||
public static string OldUserIdKey = "userId";
|
||||
public static string AddSitePromptShownKey = "addSitePromptShown";
|
||||
public static string ClearCiphersCacheKey = "clearCiphersCache";
|
||||
public static string BiometricIntegrityKey = "biometricIntegrityState";
|
||||
@@ -38,11 +26,12 @@
|
||||
public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown";
|
||||
public static string TriedV1Resync = "triedV1Resync";
|
||||
public static string EventCollectionKey = "eventCollection";
|
||||
public static string PreviousPageKey = "previousPage";
|
||||
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled";
|
||||
public static string InvalidUnlockAttempts = "invalidUnlockAttempts";
|
||||
public static string PasswordRepromptAutofillKey = "passwordRepromptAutofillKey";
|
||||
public static string PasswordVerifiedAutofillKey = "passwordVerifiedAutofillKey";
|
||||
public static string RememberEmailKey = "rememberEmail";
|
||||
public static string RememberedEmailKey = "rememberedEmail";
|
||||
public static string RememberOrgIdentifierKey = "rememberOrgIdentifier";
|
||||
public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
|
||||
public static string AppExtensionStartedKey = "appExtensionStarted";
|
||||
public static string AppExtensionActivatedKey = "appExtensionActivated";
|
||||
public const int SelectFileRequestCode = 42;
|
||||
public const int SelectFilePermissionRequestCode = 43;
|
||||
public const int SaveFileRequestCode = 44;
|
||||
@@ -58,5 +47,43 @@
|
||||
iOSAutoFillClearCiphersCacheKey,
|
||||
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}";
|
||||
}
|
||||
}
|
||||
|
||||
10
src/Core/Enums/AuthenticationStatus.cs
Normal file
10
src/Core/Enums/AuthenticationStatus.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum AuthenticationStatus : byte
|
||||
{
|
||||
LoggedOut = 0,
|
||||
Locked = 1,
|
||||
Unlocked = 2,
|
||||
Active = 3,
|
||||
}
|
||||
}
|
||||
9
src/Core/Enums/StorageLocation.cs
Normal file
9
src/Core/Enums/StorageLocation.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum StorageLocation
|
||||
{
|
||||
Both = 0,
|
||||
Disk = 1,
|
||||
Memory = 2
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Bit.App.Models
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class PreviousPageInfo
|
||||
{
|
||||
107
src/Core/Models/Domain/Account.cs
Normal file
107
src/Core/Models/Domain/Account.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Core/Models/Domain/State.cs
Normal file
10
src/Core/Models/Domain/State.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
13
src/Core/Models/Domain/StorageOptions.cs
Normal file
13
src/Core/Models/Domain/StorageOptions.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
29
src/Core/Models/View/AccountView.cs
Normal file
29
src/Core/Models/View/AccountView.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,12 @@ namespace Bit.Core.Services
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly Func<bool, Task> _logoutCallbackAsync;
|
||||
private readonly Func<Tuple<string, bool, bool>, Task> _logoutCallbackAsync;
|
||||
|
||||
public ApiService(
|
||||
ITokenService tokenService,
|
||||
IPlatformUtilsService platformUtilsService,
|
||||
Func<bool, Task> logoutCallbackAsync,
|
||||
Func<Tuple<string, bool, bool>, Task> logoutCallbackAsync,
|
||||
string customUserAgent = null)
|
||||
{
|
||||
_tokenService = tokenService;
|
||||
@@ -698,7 +698,7 @@ namespace Bit.Core.Services
|
||||
if (authed && ((tokenError && response.StatusCode == HttpStatusCode.BadRequest) ||
|
||||
response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden))
|
||||
{
|
||||
await _logoutCallbackAsync(true);
|
||||
await _logoutCallbackAsync(new Tuple<string, bool, bool>(null, false, true));
|
||||
return null;
|
||||
}
|
||||
try
|
||||
|
||||
@@ -6,33 +6,28 @@ namespace Bit.Core.Services
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
public Task<string> GetAnonymousAppIdAsync()
|
||||
{
|
||||
return MakeAndGetAppIdAsync("anonymousAppId");
|
||||
}
|
||||
|
||||
private async Task<string> MakeAndGetAppIdAsync(string key)
|
||||
{
|
||||
var existingId = await _storageService.GetAsync<string>(key);
|
||||
if (existingId != null)
|
||||
var appId = await _stateService.GetAppIdAsync();
|
||||
if (appId != null)
|
||||
{
|
||||
return existingId;
|
||||
return appId;
|
||||
}
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
await _storageService.SaveAsync(key, guid);
|
||||
return guid;
|
||||
appId = MakeAppId();
|
||||
await _stateService.SetAppIdAsync(appId);
|
||||
return appId;
|
||||
}
|
||||
|
||||
private string MakeAppId()
|
||||
{
|
||||
return Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,12 @@ namespace Bit.Core.Services
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly bool _setCryptoKeys;
|
||||
|
||||
@@ -30,7 +29,7 @@ namespace Bit.Core.Services
|
||||
ICryptoService cryptoService,
|
||||
ICryptoFunctionService cryptoFunctionService,
|
||||
IApiService apiService,
|
||||
IUserService userService,
|
||||
IStateService stateService,
|
||||
ITokenService tokenService,
|
||||
IAppIdService appIdService,
|
||||
II18nService i18nService,
|
||||
@@ -43,13 +42,12 @@ namespace Bit.Core.Services
|
||||
_cryptoService = cryptoService;
|
||||
_cryptoFunctionService = cryptoFunctionService;
|
||||
_apiService = apiService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_tokenService = tokenService;
|
||||
_appIdService = appIdService;
|
||||
_i18nService = i18nService;
|
||||
_platformUtilsService = platformUtilsService;
|
||||
_messagingService = messagingService;
|
||||
_vaultTimeoutService = vaultTimeoutService;
|
||||
_keyConnectorService = keyConnectorService;
|
||||
_setCryptoKeys = setCryptoKeys;
|
||||
|
||||
@@ -355,8 +353,24 @@ namespace Bit.Core.Services
|
||||
await _tokenService.SetTwoFactorTokenAsync(tokenResponse.TwoFactorToken, email);
|
||||
}
|
||||
await _tokenService.SetTokensAsync(tokenResponse.AccessToken, tokenResponse.RefreshToken);
|
||||
await _userService.SetInformationAsync(_tokenService.GetUserId(), _tokenService.GetEmail(),
|
||||
tokenResponse.Kdf, tokenResponse.KdfIterations);
|
||||
await _stateService.AddAccountAsync(
|
||||
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 (key != null)
|
||||
@@ -430,7 +444,7 @@ namespace Bit.Core.Services
|
||||
|
||||
}
|
||||
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
_stateService.BiometricLocked = false;
|
||||
_messagingService.Send("loggedIn");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -19,15 +19,11 @@ namespace Bit.Core.Services
|
||||
{
|
||||
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",
|
||||
"io", "co", "uk", "au", "nz", "fr", "de", "tv", "info", "app", "apps", "eu", "me", "dev", "jp", "mobile" };
|
||||
private List<CipherView> _decryptedCipherCache;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IFileUploadService _fileUploadService;
|
||||
@@ -45,7 +41,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public CipherService(
|
||||
ICryptoService cryptoService,
|
||||
IUserService userService,
|
||||
IStateService stateService,
|
||||
ISettingsService settingsService,
|
||||
IApiService apiService,
|
||||
IFileUploadService fileUploadService,
|
||||
@@ -56,7 +52,7 @@ namespace Bit.Core.Services
|
||||
string[] allClearCipherCacheKeys)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_settingsService = settingsService;
|
||||
_apiService = apiService;
|
||||
_fileUploadService = fileUploadService;
|
||||
@@ -211,11 +207,8 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<Cipher> GetAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var localData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>(
|
||||
Keys_LocalData);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
|
||||
string.Format(Keys_CiphersFormat, userId));
|
||||
var localData = await _stateService.GetLocalDataAsync();
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (!ciphers?.ContainsKey(id) ?? true)
|
||||
{
|
||||
return null;
|
||||
@@ -226,11 +219,8 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<List<Cipher>> GetAllAsync()
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var localData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>(
|
||||
Keys_LocalData);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
|
||||
string.Format(Keys_CiphersFormat, userId));
|
||||
var localData = await _stateService.GetLocalDataAsync();
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
var response = ciphers?.Select(c => new Cipher(c.Value, false,
|
||||
localData?.ContainsKey(c.Key) ?? false ? localData[c.Key] : null));
|
||||
return response?.ToList() ?? new List<Cipher>();
|
||||
@@ -347,7 +337,7 @@ namespace Bit.Core.Services
|
||||
var others = new List<CipherView>();
|
||||
var ciphers = await ciphersTask;
|
||||
|
||||
var defaultMatch = (UriMatchType?)(await _storageService.GetAsync<int?>(Constants.DefaultUriMatch));
|
||||
var defaultMatch = (UriMatchType?)(await _stateService.GetDefaultUriMatchAsync());
|
||||
if (defaultMatch == null)
|
||||
{
|
||||
defaultMatch = UriMatchType.Domain;
|
||||
@@ -457,8 +447,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task UpdateLastUsedDateAsync(string id)
|
||||
{
|
||||
var ciphersLocalData = await _storageService.GetAsync<Dictionary<string, Dictionary<string, object>>>(
|
||||
Keys_LocalData);
|
||||
var ciphersLocalData = await _stateService.GetLocalDataAsync();
|
||||
if (ciphersLocalData == null)
|
||||
{
|
||||
ciphersLocalData = new Dictionary<string, Dictionary<string, object>>();
|
||||
@@ -476,7 +465,7 @@ namespace Bit.Core.Services
|
||||
ciphersLocalData[id].Add("lastUsedDate", DateTime.UtcNow);
|
||||
}
|
||||
|
||||
await _storageService.SaveAsync(Keys_LocalData, ciphersLocalData);
|
||||
await _stateService.SetLocalDataAsync(ciphersLocalData);
|
||||
// Update cache
|
||||
if (DecryptedCipherCache == null)
|
||||
{
|
||||
@@ -495,13 +484,13 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return;
|
||||
}
|
||||
var domains = await _storageService.GetAsync<HashSet<string>>(Keys_NeverDomains);
|
||||
var domains = await _stateService.GetNeverDomainsAsync();
|
||||
if (domains == null)
|
||||
{
|
||||
domains = new HashSet<string>();
|
||||
}
|
||||
domains.Add(domain);
|
||||
await _storageService.SaveAsync(Keys_NeverDomains, domains);
|
||||
await _stateService.SetNeverDomainsAsync(domains);
|
||||
}
|
||||
|
||||
public async Task SaveWithServerAsync(Cipher cipher)
|
||||
@@ -526,7 +515,7 @@ namespace Bit.Core.Services
|
||||
var request = new CipherRequest(cipher);
|
||||
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);
|
||||
await UpsertAsync(data);
|
||||
}
|
||||
@@ -550,7 +539,7 @@ namespace Bit.Core.Services
|
||||
var encCipher = await EncryptAsync(cipher);
|
||||
var request = new CipherShareRequest(encCipher);
|
||||
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);
|
||||
await UpsertAsync(data);
|
||||
}
|
||||
@@ -581,7 +570,7 @@ namespace Bit.Core.Services
|
||||
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);
|
||||
await UpsertAsync(cData);
|
||||
return new Cipher(cData);
|
||||
@@ -602,16 +591,14 @@ namespace Bit.Core.Services
|
||||
{
|
||||
var request = new CipherCollectionsRequest(cipher.CollectionIds?.ToList());
|
||||
await _apiService.PutCipherCollectionsAsync(cipher.Id, request);
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
var data = cipher.ToCipherData(userId);
|
||||
await UpsertAsync(data);
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(CipherData cipher)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var storageKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(storageKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null)
|
||||
{
|
||||
ciphers = new Dictionary<string, CipherData>();
|
||||
@@ -621,15 +608,13 @@ namespace Bit.Core.Services
|
||||
ciphers.Add(cipher.Id, null);
|
||||
}
|
||||
ciphers[cipher.Id] = cipher;
|
||||
await _storageService.SaveAsync(storageKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(List<CipherData> cipher)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var storageKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(storageKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null)
|
||||
{
|
||||
ciphers = new Dictionary<string, CipherData>();
|
||||
@@ -642,28 +627,25 @@ namespace Bit.Core.Services
|
||||
}
|
||||
ciphers[c.Id] = c;
|
||||
}
|
||||
await _storageService.SaveAsync(storageKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
public async Task ReplaceAsync(Dictionary<string, CipherData> ciphers)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
await _storageService.SaveAsync(string.Format(Keys_CiphersFormat, userId), ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
public async Task ClearAsync(string userId)
|
||||
{
|
||||
await _storageService.RemoveAsync(string.Format(Keys_CiphersFormat, userId));
|
||||
await _stateService.SetEncryptedCiphersAsync(null, userId);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var cipherKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null)
|
||||
{
|
||||
return;
|
||||
@@ -673,15 +655,13 @@ namespace Bit.Core.Services
|
||||
return;
|
||||
}
|
||||
ciphers.Remove(id);
|
||||
await _storageService.SaveAsync(cipherKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(List<string> ids)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var cipherKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null)
|
||||
{
|
||||
return;
|
||||
@@ -694,7 +674,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
ciphers.Remove(id);
|
||||
}
|
||||
await _storageService.SaveAsync(cipherKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
@@ -706,9 +686,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task DeleteAttachmentAsync(string id, string attachmentId)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var cipherKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null || !ciphers.ContainsKey(id) || ciphers[id].Attachments == null)
|
||||
{
|
||||
return;
|
||||
@@ -718,7 +696,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
ciphers[id].Attachments.Remove(attachment);
|
||||
}
|
||||
await _storageService.SaveAsync(cipherKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
@@ -771,9 +749,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task SoftDeleteWithServerAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var cipherKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null)
|
||||
{
|
||||
return;
|
||||
@@ -785,15 +761,13 @@ namespace Bit.Core.Services
|
||||
|
||||
await _apiService.PutDeleteCipherAsync(id);
|
||||
ciphers[id].DeletedDate = DateTime.UtcNow;
|
||||
await _storageService.SaveAsync(cipherKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
public async Task RestoreWithServerAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var cipherKey = string.Format(Keys_CiphersFormat, userId);
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(cipherKey);
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers == null)
|
||||
{
|
||||
return;
|
||||
@@ -805,7 +779,7 @@ namespace Bit.Core.Services
|
||||
var response = await _apiService.PutRestoreCipherAsync(id);
|
||||
ciphers[id].DeletedDate = null;
|
||||
ciphers[id].RevisionDate = response.RevisionDate;
|
||||
await _storageService.SaveAsync(cipherKey, ciphers);
|
||||
await _stateService.SetEncryptedCiphersAsync(ciphers);
|
||||
await ClearCacheAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,24 +13,20 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class CollectionService : ICollectionService
|
||||
{
|
||||
private const string Keys_CollectionsFormat = "collections_{0}";
|
||||
private const char NestingDelimiter = '/';
|
||||
|
||||
private List<CollectionView> _decryptedCollectionCache;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly II18nService _i18nService;
|
||||
|
||||
public CollectionService(
|
||||
ICryptoService cryptoService,
|
||||
IUserService userService,
|
||||
IStorageService storageService,
|
||||
IStateService stateService,
|
||||
II18nService i18nService)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_userService = userService;
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
_i18nService = i18nService;
|
||||
}
|
||||
|
||||
@@ -83,9 +79,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<Collection> GetAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(
|
||||
string.Format(Keys_CollectionsFormat, userId));
|
||||
var collections = await _stateService.GetEncryptedCollectionsAsync();
|
||||
if (!collections?.ContainsKey(id) ?? true)
|
||||
{
|
||||
return null;
|
||||
@@ -95,9 +89,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<List<Collection>> GetAllAsync()
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(
|
||||
string.Format(Keys_CollectionsFormat, userId));
|
||||
var collections = await _stateService.GetEncryptedCollectionsAsync();
|
||||
var response = collections?.Select(c => new Collection(c.Value));
|
||||
return response?.ToList() ?? new List<Collection>();
|
||||
}
|
||||
@@ -148,9 +140,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task UpsertAsync(CollectionData collection)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var storageKey = string.Format(Keys_CollectionsFormat, userId);
|
||||
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(storageKey);
|
||||
var collections = await _stateService.GetEncryptedCollectionsAsync();
|
||||
if (collections == null)
|
||||
{
|
||||
collections = new Dictionary<string, CollectionData>();
|
||||
@@ -160,15 +150,13 @@ namespace Bit.Core.Services
|
||||
collections.Add(collection.Id, null);
|
||||
}
|
||||
collections[collection.Id] = collection;
|
||||
await _storageService.SaveAsync(storageKey, collections);
|
||||
await _stateService.SetEncryptedCollectionsAsync(collections);
|
||||
_decryptedCollectionCache = null;
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(List<CollectionData> collection)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var storageKey = string.Format(Keys_CollectionsFormat, userId);
|
||||
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(storageKey);
|
||||
var collections = await _stateService.GetEncryptedCollectionsAsync();
|
||||
if (collections == null)
|
||||
{
|
||||
collections = new Dictionary<string, CollectionData>();
|
||||
@@ -181,34 +169,31 @@ namespace Bit.Core.Services
|
||||
}
|
||||
collections[c.Id] = c;
|
||||
}
|
||||
await _storageService.SaveAsync(storageKey, collections);
|
||||
await _stateService.SetEncryptedCollectionsAsync(collections);
|
||||
_decryptedCollectionCache = null;
|
||||
}
|
||||
|
||||
public async Task ReplaceAsync(Dictionary<string, CollectionData> collections)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
await _storageService.SaveAsync(string.Format(Keys_CollectionsFormat, userId), collections);
|
||||
await _stateService.SetEncryptedCollectionsAsync(collections);
|
||||
_decryptedCollectionCache = null;
|
||||
}
|
||||
|
||||
public async Task ClearAsync(string userId)
|
||||
{
|
||||
await _storageService.RemoveAsync(string.Format(Keys_CollectionsFormat, userId));
|
||||
await _stateService.SetEncryptedCollectionsAsync(null, userId);
|
||||
_decryptedCollectionCache = null;
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var collectionKey = string.Format(Keys_CollectionsFormat, userId);
|
||||
var collections = await _storageService.GetAsync<Dictionary<string, CollectionData>>(collectionKey);
|
||||
var collections = await _stateService.GetEncryptedCollectionsAsync();
|
||||
if (collections == null || !collections.ContainsKey(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
collections.Remove(id);
|
||||
await _storageService.SaveAsync(collectionKey, collections);
|
||||
await _stateService.SetEncryptedCollectionsAsync(collections);
|
||||
_decryptedCollectionCache = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,9 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class CryptoService : ICryptoService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
|
||||
private SymmetricCryptoKey _key;
|
||||
private SymmetricCryptoKey _encKey;
|
||||
private SymmetricCryptoKey _legacyEtmKey;
|
||||
private string _keyHash;
|
||||
@@ -28,39 +26,31 @@ namespace Bit.Core.Services
|
||||
private Task<SymmetricCryptoKey> _getEncKeysTask;
|
||||
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(
|
||||
IStorageService storageService,
|
||||
IStorageService secureStorageService,
|
||||
IStateService stateService,
|
||||
ICryptoFunctionService cryptoFunctionService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_secureStorageService = secureStorageService;
|
||||
_stateService = stateService;
|
||||
_cryptoFunctionService = cryptoFunctionService;
|
||||
}
|
||||
|
||||
public async Task SetKeyAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
_key = key;
|
||||
var option = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var biometric = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey);
|
||||
await _stateService.SetKeyDecryptedAsync(key);
|
||||
var option = await _stateService.GetVaultTimeoutAsync();
|
||||
var biometric = await _stateService.GetBiometricUnlockAsync();
|
||||
if (option.HasValue && !biometric.GetValueOrDefault())
|
||||
{
|
||||
// If we have a lock option set, we do not store the key
|
||||
return;
|
||||
}
|
||||
await _secureStorageService.SaveAsync(Keys_Key, key?.KeyB64);
|
||||
await _stateService.SetKeyEncryptedAsync(key?.KeyB64);
|
||||
}
|
||||
|
||||
public async Task SetKeyHashAsync(string keyHash)
|
||||
{
|
||||
_keyHash = keyHash;
|
||||
await _storageService.SaveAsync(Keys_KeyHash, keyHash);
|
||||
await _stateService.SetKeyHashAsync(keyHash);
|
||||
}
|
||||
|
||||
public async Task SetEncKeyAsync(string encKey)
|
||||
@@ -69,7 +59,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _storageService.SaveAsync(Keys_EncKey, encKey);
|
||||
await _stateService.SetEncKeyEncryptedAsync(encKey);
|
||||
_encKey = null;
|
||||
}
|
||||
|
||||
@@ -79,7 +69,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _storageService.SaveAsync(Keys_EncPrivateKey, encPrivateKey);
|
||||
await _stateService.SetPrivateKeyEncryptedAsync(encPrivateKey);
|
||||
_privateKey = null;
|
||||
}
|
||||
|
||||
@@ -87,21 +77,23 @@ namespace Bit.Core.Services
|
||||
{
|
||||
var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key);
|
||||
_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)
|
||||
{
|
||||
_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()
|
||||
@@ -110,7 +102,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return _keyHash;
|
||||
}
|
||||
var keyHash = await _storageService.GetAsync<string>(Keys_KeyHash);
|
||||
var keyHash = await _stateService.GetKeyHashAsync();
|
||||
if (keyHash != null)
|
||||
{
|
||||
_keyHash = keyHash;
|
||||
@@ -132,7 +124,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var encKey = await _storageService.GetAsync<string>(Keys_EncKey);
|
||||
var encKey = await _stateService.GetEncKeyEncryptedAsync();
|
||||
if (encKey == null)
|
||||
{
|
||||
return null;
|
||||
@@ -200,7 +192,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return _privateKey;
|
||||
}
|
||||
var encPrivateKey = await _storageService.GetAsync<string>(Keys_EncPrivateKey);
|
||||
var encPrivateKey = await _stateService.GetPrivateKeyEncryptedAsync();
|
||||
if (encPrivateKey == null)
|
||||
{
|
||||
return null;
|
||||
@@ -238,7 +230,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var encOrgKeys = await _storageService.GetAsync<Dictionary<string, string>>(Keys_EncOrgKeys);
|
||||
var encOrgKeys = await _stateService.GetOrgKeysEncryptedAsync();
|
||||
if (encOrgKeys == null)
|
||||
{
|
||||
return null;
|
||||
@@ -303,84 +295,95 @@ namespace Bit.Core.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> HasKeyAsync()
|
||||
public async Task<bool> HasKeyAsync(string userId = null)
|
||||
{
|
||||
var key = await GetKeyAsync();
|
||||
var key = await GetKeyAsync(userId);
|
||||
return key != null;
|
||||
}
|
||||
|
||||
public async Task<bool> HasEncKeyAsync()
|
||||
{
|
||||
var encKey = await _storageService.GetAsync<string>(Keys_EncKey);
|
||||
var encKey = await _stateService.GetEncKeyEncryptedAsync();
|
||||
return encKey != null;
|
||||
}
|
||||
|
||||
public async Task ClearKeyAsync()
|
||||
public async Task ClearKeyAsync(string userId = null)
|
||||
{
|
||||
_key = _legacyEtmKey = null;
|
||||
await _secureStorageService.RemoveAsync(Keys_Key);
|
||||
await _stateService.SetKeyDecryptedAsync(null, userId);
|
||||
_legacyEtmKey = null;
|
||||
await _stateService.SetKeyEncryptedAsync(null, userId);
|
||||
}
|
||||
|
||||
public async Task ClearKeyHashAsync()
|
||||
public async Task ClearKeyHashAsync(string userId = 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;
|
||||
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;
|
||||
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;
|
||||
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[]
|
||||
{
|
||||
ClearKeyAsync(),
|
||||
ClearKeyHashAsync(),
|
||||
ClearOrgKeysAsync(),
|
||||
ClearEncKeyAsync(),
|
||||
ClearKeyPairAsync(),
|
||||
ClearPinProtectedKeyAsync()
|
||||
ClearKeyAsync(userId),
|
||||
ClearKeyHashAsync(userId),
|
||||
ClearOrgKeysAsync(false, userId),
|
||||
ClearEncKeyAsync(false, userId),
|
||||
ClearKeyPairAsync(false, userId),
|
||||
ClearPinProtectedKeyAsync(userId)
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ToggleKeyAsync()
|
||||
{
|
||||
var key = await GetKeyAsync();
|
||||
var option = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var biometric = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey);
|
||||
var option = await _stateService.GetVaultTimeoutAsync();
|
||||
var biometric = await _stateService.GetBiometricUnlockAsync();
|
||||
if (!biometric.GetValueOrDefault() && (option != null || option == 0))
|
||||
{
|
||||
await ClearKeyAsync();
|
||||
_key = key;
|
||||
await _stateService.SetKeyDecryptedAsync(key);
|
||||
return;
|
||||
}
|
||||
await SetKeyAsync(key);
|
||||
@@ -415,7 +418,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if (protectedKeyCs == null)
|
||||
{
|
||||
var pinProtectedKey = await _storageService.GetAsync<string>(Constants.PinProtectedKey);
|
||||
var pinProtectedKey = await _stateService.GetPinProtectedAsync();
|
||||
if (pinProtectedKey == null)
|
||||
{
|
||||
throw new Exception("No PIN protected key found.");
|
||||
|
||||
@@ -9,14 +9,14 @@ namespace Bit.Core.Services
|
||||
public class EnvironmentService : IEnvironmentService
|
||||
{
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
|
||||
public EnvironmentService(
|
||||
IApiService apiService,
|
||||
IStorageService storageService)
|
||||
IStateService stateService)
|
||||
{
|
||||
_apiService = apiService;
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
}
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
@@ -42,7 +42,11 @@ namespace Bit.Core.Services
|
||||
|
||||
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)
|
||||
{
|
||||
urls = new EnvironmentUrlData();
|
||||
@@ -72,7 +76,7 @@ namespace Bit.Core.Services
|
||||
urls.Icons = FormatUrl(urls.Icons);
|
||||
urls.Notifications = FormatUrl(urls.Notifications);
|
||||
urls.Events = FormatUrl(urls.Events);
|
||||
await _storageService.SaveAsync(Constants.EnvironmentUrlsKey, urls);
|
||||
await _stateService.SetPreAuthEnvironmentUrlsAsync(urls);
|
||||
BaseUrl = urls.Base;
|
||||
WebVaultUrl = urls.WebVault;
|
||||
ApiUrl = urls.Api;
|
||||
|
||||
@@ -12,31 +12,31 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class EventService : IEventService
|
||||
{
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly ICipherService _cipherService;
|
||||
|
||||
public EventService(
|
||||
IStorageService storageService,
|
||||
IApiService apiService,
|
||||
IUserService userService,
|
||||
IStateService stateService,
|
||||
IOrganizationService organizationService,
|
||||
ICipherService cipherService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_apiService = apiService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_organizationService = organizationService;
|
||||
_cipherService = cipherService;
|
||||
}
|
||||
|
||||
public async Task CollectAsync(EventType eventType, string cipherId = null, bool uploadImmediately = false)
|
||||
{
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var organizations = await _userService.GetAllOrganizationAsync();
|
||||
var organizations = await _organizationService.GetAllAsync();
|
||||
if (organizations == null)
|
||||
{
|
||||
return;
|
||||
@@ -54,7 +54,7 @@ namespace Bit.Core.Services
|
||||
return;
|
||||
}
|
||||
}
|
||||
var eventCollection = await _storageService.GetAsync<List<EventData>>(Constants.EventCollectionKey);
|
||||
var eventCollection = await _stateService.GetEventCollectionAsync();
|
||||
if (eventCollection == null)
|
||||
{
|
||||
eventCollection = new List<EventData>();
|
||||
@@ -65,7 +65,7 @@ namespace Bit.Core.Services
|
||||
CipherId = cipherId,
|
||||
Date = DateTime.UtcNow
|
||||
});
|
||||
await _storageService.SaveAsync(Constants.EventCollectionKey, eventCollection);
|
||||
await _stateService.SetEventCollectionAsync(eventCollection);
|
||||
if (uploadImmediately)
|
||||
{
|
||||
await UploadEventsAsync();
|
||||
@@ -74,12 +74,12 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task UploadEventsAsync()
|
||||
{
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (!authed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var eventCollection = await _storageService.GetAsync<List<EventData>>(Constants.EventCollectionKey);
|
||||
var eventCollection = await _stateService.GetEventCollectionAsync();
|
||||
if (eventCollection == null || !eventCollection.Any())
|
||||
{
|
||||
return;
|
||||
@@ -100,7 +100,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task ClearEventsAsync()
|
||||
{
|
||||
await _storageService.RemoveAsync(Constants.EventCollectionKey);
|
||||
await _stateService.SetEventCollectionAsync(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,30 +15,25 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class FolderService : IFolderService
|
||||
{
|
||||
private const string Keys_CiphersFormat = "ciphers_{0}";
|
||||
private const string Keys_FoldersFormat = "folders_{0}";
|
||||
private const char NestingDelimiter = '/';
|
||||
|
||||
private List<FolderView> _decryptedFolderCache;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly ICipherService _cipherService;
|
||||
|
||||
public FolderService(
|
||||
ICryptoService cryptoService,
|
||||
IUserService userService,
|
||||
IStateService stateService,
|
||||
IApiService apiService,
|
||||
IStorageService storageService,
|
||||
II18nService i18nService,
|
||||
ICipherService cipherService)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_apiService = apiService;
|
||||
_storageService = storageService;
|
||||
_i18nService = i18nService;
|
||||
_cipherService = cipherService;
|
||||
}
|
||||
@@ -60,9 +55,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<Folder> GetAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(
|
||||
string.Format(Keys_FoldersFormat, userId));
|
||||
var folders = await _stateService.GetEncryptedFoldersAsync();
|
||||
if (!folders?.ContainsKey(id) ?? true)
|
||||
{
|
||||
return null;
|
||||
@@ -72,9 +65,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<List<Folder>> GetAllAsync()
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(
|
||||
string.Format(Keys_FoldersFormat, userId));
|
||||
var folders = await _stateService.GetEncryptedFoldersAsync();
|
||||
var response = folders?.Select(f => new Folder(f.Value));
|
||||
return response?.ToList() ?? new List<Folder>();
|
||||
}
|
||||
@@ -153,16 +144,14 @@ namespace Bit.Core.Services
|
||||
{
|
||||
response = await _apiService.PutFolderAsync(folder.Id, request);
|
||||
}
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
var data = new FolderData(response, userId);
|
||||
await UpsertAsync(data);
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(FolderData folder)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var storageKey = string.Format(Keys_FoldersFormat, userId);
|
||||
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(storageKey);
|
||||
var folders = await _stateService.GetEncryptedFoldersAsync();
|
||||
if (folders == null)
|
||||
{
|
||||
folders = new Dictionary<string, FolderData>();
|
||||
@@ -172,15 +161,13 @@ namespace Bit.Core.Services
|
||||
folders.Add(folder.Id, null);
|
||||
}
|
||||
folders[folder.Id] = folder;
|
||||
await _storageService.SaveAsync(storageKey, folders);
|
||||
await _stateService.SetEncryptedFoldersAsync(folders);
|
||||
_decryptedFolderCache = null;
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(List<FolderData> folder)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var storageKey = string.Format(Keys_FoldersFormat, userId);
|
||||
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(storageKey);
|
||||
var folders = await _stateService.GetEncryptedFoldersAsync();
|
||||
if (folders == null)
|
||||
{
|
||||
folders = new Dictionary<string, FolderData>();
|
||||
@@ -193,39 +180,35 @@ namespace Bit.Core.Services
|
||||
}
|
||||
folders[f.Id] = f;
|
||||
}
|
||||
await _storageService.SaveAsync(storageKey, folders);
|
||||
await _stateService.SetEncryptedFoldersAsync(folders);
|
||||
_decryptedFolderCache = null;
|
||||
}
|
||||
|
||||
public async Task ReplaceAsync(Dictionary<string, FolderData> folders)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
await _storageService.SaveAsync(string.Format(Keys_FoldersFormat, userId), folders);
|
||||
await _stateService.SetEncryptedFoldersAsync(folders);
|
||||
_decryptedFolderCache = null;
|
||||
}
|
||||
|
||||
public async Task ClearAsync(string userId)
|
||||
{
|
||||
await _storageService.RemoveAsync(string.Format(Keys_FoldersFormat, userId));
|
||||
await _stateService.SetEncryptedFoldersAsync(null, userId);
|
||||
_decryptedFolderCache = null;
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var folderKey = string.Format(Keys_FoldersFormat, userId);
|
||||
var folders = await _storageService.GetAsync<Dictionary<string, FolderData>>(folderKey);
|
||||
var folders = await _stateService.GetEncryptedFoldersAsync();
|
||||
if (folders == null || !folders.ContainsKey(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
folders.Remove(id);
|
||||
await _storageService.SaveAsync(folderKey, folders);
|
||||
await _stateService.SetEncryptedFoldersAsync(folders);
|
||||
_decryptedFolderCache = null;
|
||||
|
||||
// Items in a deleted folder are re-assigned to "No Folder"
|
||||
var ciphers = await _storageService.GetAsync<Dictionary<string, CipherData>>(
|
||||
string.Format(Keys_CiphersFormat, userId));
|
||||
var ciphers = await _stateService.GetEncryptedCiphersAsync();
|
||||
if (ciphers != null)
|
||||
{
|
||||
var updates = new List<CipherData>();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
|
||||
@@ -9,24 +8,20 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class KeyConnectorService : IKeyConnectorService
|
||||
{
|
||||
private const string Keys_UsesKeyConnector = "usesKeyConnector";
|
||||
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
|
||||
private bool? _usesKeyConnector;
|
||||
|
||||
public KeyConnectorService(IUserService userService, ICryptoService cryptoService,
|
||||
IStorageService storageService, ITokenService tokenService, IApiService apiService)
|
||||
public KeyConnectorService(IStateService stateService, ICryptoService cryptoService,
|
||||
ITokenService tokenService, IApiService apiService, OrganizationService organizationService)
|
||||
{
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_cryptoService = cryptoService;
|
||||
_storageService = storageService;
|
||||
_tokenService = tokenService;
|
||||
_apiService = apiService;
|
||||
_organizationService = organizationService;
|
||||
}
|
||||
|
||||
public async Task GetAndSetKey(string url)
|
||||
@@ -46,23 +41,17 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task SetUsesKeyConnector(bool usesKeyConnector)
|
||||
{
|
||||
_usesKeyConnector = usesKeyConnector;
|
||||
await _storageService.SaveAsync(Keys_UsesKeyConnector, usesKeyConnector);
|
||||
await _stateService.SetUsesKeyConnectorAsync(usesKeyConnector);
|
||||
}
|
||||
|
||||
public async Task<bool> GetUsesKeyConnector()
|
||||
{
|
||||
if (!_usesKeyConnector.HasValue)
|
||||
{
|
||||
_usesKeyConnector = await _storageService.GetAsync<bool>(Keys_UsesKeyConnector);
|
||||
}
|
||||
|
||||
return _usesKeyConnector.Value;
|
||||
return await _stateService.GetUsesKeyConnectorAsync();
|
||||
}
|
||||
|
||||
public async Task<Organization> GetManagingOrganization()
|
||||
{
|
||||
var orgs = await _userService.GetAllOrganizationAsync();
|
||||
var orgs = await _organizationService.GetAllAsync();
|
||||
return orgs.Find(o =>
|
||||
o.UsesKeyConnector &&
|
||||
!o.IsAdmin);
|
||||
@@ -88,7 +77,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<bool> UserNeedsMigration()
|
||||
{
|
||||
var loggedInUsingSso = _tokenService.GetIsExternal();
|
||||
var loggedInUsingSso = await _tokenService.GetIsExternal();
|
||||
var requiredByOrganization = await GetManagingOrganization() != null;
|
||||
var userIsNotUsingKeyConnector = !await GetUsesKeyConnector();
|
||||
|
||||
|
||||
55
src/Core/Services/OrganizationService.cs
Normal file
55
src/Core/Services/OrganizationService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class PasswordGenerationService : IPasswordGenerationService
|
||||
{
|
||||
private const string Keys_Options = "passwordGenerationOptions";
|
||||
private const string Keys_History = "generatedPasswordHistory";
|
||||
private const int MaxPasswordsInHistory = 100;
|
||||
private const string LowercaseCharSet = "abcdefghijkmnopqrstuvwxyz";
|
||||
private const string UppercaseCharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ";
|
||||
@@ -23,7 +21,7 @@ namespace Bit.Core.Services
|
||||
private const string SpecialCharSet = "!@#$%^&*";
|
||||
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private PasswordGenerationOptions _defaultOptions = new PasswordGenerationOptions(true);
|
||||
@@ -32,12 +30,12 @@ namespace Bit.Core.Services
|
||||
|
||||
public PasswordGenerationService(
|
||||
ICryptoService cryptoService,
|
||||
IStorageService storageService,
|
||||
IStateService stateService,
|
||||
ICryptoFunctionService cryptoFunctionService,
|
||||
IPolicyService policyService)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_storageService = storageService;
|
||||
_stateService = stateService;
|
||||
_cryptoFunctionService = cryptoFunctionService;
|
||||
_policyService = policyService;
|
||||
}
|
||||
@@ -160,6 +158,12 @@ namespace Bit.Core.Services
|
||||
return password.ToString();
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
_optionsCache = null;
|
||||
_history = null;
|
||||
}
|
||||
|
||||
public async Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options)
|
||||
{
|
||||
options.Merge(_defaultOptions);
|
||||
@@ -204,7 +208,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if (_optionsCache == null)
|
||||
{
|
||||
var options = await _storageService.GetAsync<PasswordGenerationOptions>(Keys_Options);
|
||||
var options = await _stateService.GetPasswordGenerationOptionsAsync();
|
||||
if (options == null)
|
||||
{
|
||||
_optionsCache = _defaultOptions;
|
||||
@@ -432,7 +436,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task SaveOptionsAsync(PasswordGenerationOptions options)
|
||||
{
|
||||
await _storageService.SaveAsync(Keys_Options, options);
|
||||
await _stateService.SetPasswordGenerationOptionsAsync(options);
|
||||
_optionsCache = options;
|
||||
}
|
||||
|
||||
@@ -445,7 +449,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
if (_history == null)
|
||||
{
|
||||
var encrypted = await _storageService.GetAsync<List<GeneratedPasswordHistory>>(Keys_History);
|
||||
var encrypted = await _stateService.GetEncryptedPasswordGenerationHistory();
|
||||
_history = await DecryptHistoryAsync(encrypted);
|
||||
}
|
||||
return _history ?? new List<GeneratedPasswordHistory>();
|
||||
@@ -473,13 +477,13 @@ namespace Bit.Core.Services
|
||||
}
|
||||
var newHistory = await EncryptHistoryAsync(currentHistory);
|
||||
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>();
|
||||
await _storageService.RemoveAsync(Keys_History);
|
||||
await _stateService.SetEncryptedPasswordGenerationHistoryAsync(null, userId);
|
||||
}
|
||||
|
||||
public Result PasswordStrength(string password, List<string> userInputs = null)
|
||||
|
||||
@@ -12,19 +12,17 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public class PolicyService : IPolicyService
|
||||
{
|
||||
private const string Keys_PoliciesPrefix = "policies_{0}";
|
||||
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
|
||||
private IEnumerable<Policy> _policyCache;
|
||||
|
||||
public PolicyService(
|
||||
IStorageService storageService,
|
||||
IUserService userService)
|
||||
IStateService stateService,
|
||||
IOrganizationService organizationService)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_organizationService = organizationService;
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
@@ -36,9 +34,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if (_policyCache == null)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var policies = await _storageService.GetAsync<Dictionary<string, PolicyData>>(
|
||||
string.Format(Keys_PoliciesPrefix, userId));
|
||||
var policies = await _stateService.GetEncryptedPoliciesAsync();
|
||||
if (policies == null)
|
||||
{
|
||||
return null;
|
||||
@@ -58,14 +54,13 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task Replace(Dictionary<string, PolicyData> policies)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
await _storageService.SaveAsync(string.Format(Keys_PoliciesPrefix, userId), policies);
|
||||
await _stateService.SetEncryptedPoliciesAsync(policies);
|
||||
_policyCache = null;
|
||||
}
|
||||
|
||||
public async Task Clear(string userId)
|
||||
{
|
||||
await _storageService.RemoveAsync(string.Format(Keys_PoliciesPrefix, userId));
|
||||
await _stateService.SetEncryptedPoliciesAsync(null, userId);
|
||||
_policyCache = null;
|
||||
}
|
||||
|
||||
@@ -205,7 +200,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var organizations = await _userService.GetAllOrganizationAsync();
|
||||
var organizations = await _organizationService.GetAllAsync();
|
||||
|
||||
IEnumerable<Policy> filteredPolicies;
|
||||
|
||||
|
||||
@@ -20,9 +20,8 @@ namespace Bit.Core.Services
|
||||
{
|
||||
private List<SendView> _decryptedSendsCache;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private Task<List<SendView>> _getAllDecryptedTask;
|
||||
@@ -30,27 +29,23 @@ namespace Bit.Core.Services
|
||||
|
||||
public SendService(
|
||||
ICryptoService cryptoService,
|
||||
IUserService userService,
|
||||
IStateService stateService,
|
||||
IApiService apiService,
|
||||
IFileUploadService fileUploadService,
|
||||
IStorageService storageService,
|
||||
II18nService i18nService,
|
||||
ICryptoFunctionService cryptoFunctionService)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_userService = userService;
|
||||
_stateService = stateService;
|
||||
_apiService = apiService;
|
||||
_fileUploadService = fileUploadService;
|
||||
_storageService = storageService;
|
||||
_i18nService = i18nService;
|
||||
_cryptoFunctionService = cryptoFunctionService;
|
||||
}
|
||||
|
||||
public static string GetSendKey(string userId) => string.Format("sends_{0}", userId);
|
||||
|
||||
public async Task ClearAsync(string userId)
|
||||
{
|
||||
await _storageService.RemoveAsync(GetSendKey(userId));
|
||||
await _stateService.SetEncryptedSendsAsync(null, userId);
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
@@ -58,8 +53,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task DeleteAsync(params string[] ids)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var sends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId));
|
||||
var sends = await _stateService.GetEncryptedSendsAsync();
|
||||
|
||||
if (sends == null)
|
||||
{
|
||||
@@ -71,7 +65,7 @@ namespace Bit.Core.Services
|
||||
sends.Remove(id);
|
||||
}
|
||||
|
||||
await _storageService.SaveAsync(GetSendKey(userId), sends);
|
||||
await _stateService.SetEncryptedSendsAsync(sends);
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
@@ -138,8 +132,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<List<Send>> GetAllAsync()
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var sends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId));
|
||||
var sends = await _stateService.GetEncryptedSendsAsync();
|
||||
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)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var sends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId));
|
||||
var sends = await _stateService.GetEncryptedSendsAsync();
|
||||
|
||||
if (sends == null || !sends.ContainsKey(id))
|
||||
{
|
||||
@@ -192,8 +184,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task ReplaceAsync(Dictionary<string, SendData> sends)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
await _storageService.SaveAsync(GetSendKey(userId), sends);
|
||||
await _stateService.SetEncryptedSendsAsync(sends);
|
||||
_decryptedSendsCache = null;
|
||||
}
|
||||
|
||||
@@ -237,7 +228,7 @@ namespace Bit.Core.Services
|
||||
response = await _apiService.PutSendAsync(send.Id, request);
|
||||
}
|
||||
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
await UpsertAsync(new SendData(response, userId));
|
||||
return response.Id;
|
||||
}
|
||||
@@ -255,8 +246,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task UpsertAsync(params SendData[] sends)
|
||||
{
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var knownSends = await _storageService.GetAsync<Dictionary<string, SendData>>(GetSendKey(userId)) ??
|
||||
var knownSends = await _stateService.GetEncryptedSendsAsync() ??
|
||||
new Dictionary<string, SendData>();
|
||||
|
||||
foreach (var send in sends)
|
||||
@@ -264,14 +254,14 @@ namespace Bit.Core.Services
|
||||
knownSends[send.Id] = send;
|
||||
}
|
||||
|
||||
await _storageService.SaveAsync(GetSendKey(userId), knownSends);
|
||||
await _stateService.SetEncryptedSendsAsync(knownSends);
|
||||
_decryptedSendsCache = null;
|
||||
}
|
||||
|
||||
public async Task RemovePasswordWithServerAsync(string id)
|
||||
{
|
||||
var response = await _apiService.PutSendRemovePasswordAsync(id);
|
||||
var userId = await _userService.GetUserIdAsync();
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
await UpsertAsync(new SendData(response, userId));
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user