mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 02:48:29 +03:00
[KeyConnector] Add support for key connector OTP (#1633)
* initial commit - add UsesKeyConnector to UserService - add models - begin work on authentication * finish auth workflow for key connector sso login - finish api call for get user key - start api calls for posts to key connector * Bypass lock page if already unlocked * Move logic to KeyConnectorService, log out if no pin or biometric is set * Disable password reprompt when using key connector * hide password reprompt checkbox when editing or adding cipher * add PostUserKey and PostSetKeyConnector calls * add ConvertMasterPasswordPage * add functionality to RemoveMasterPasswordPage - rename Convert to Remove * Hide Change Master Password button if using key connector * Add OTP verification for export component * Update src/App/Pages/Vault/AddEditPage.xaml.cs Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * remove toolbar item "close" * Update src/Core/Models/Request/KeyConnectorUserKeyRequest.cs Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * remove new line in resource string - format warning as two labels - set label in code behind for loading simultaneously * implement GetAndSetKey in KeyConnectorService - ignore EnvironmentService call * remove unnecesary orgIdentifier * move RemoveMasterPasswordPage call to LockPage * add spacing to export vault page * log out if no PIN or bio on lock page with key connector * Delete excessive whitespace * Delete excessive whitespace * Change capitalisation of OTP * add default value to models for backwards compatibility * remove this keyword * actually handle exceptions * move RemoveMasterPasswordPage to TabPage using messaging service * add minor improvements * remove 'this.' Co-authored-by: Hinton <oscar@oscarhinton.com> Co-authored-by: Thomas Rittson <trittson@bitwarden.com> Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
parent
90b62d61ae
commit
13869b5a1b
40 changed files with 869 additions and 74 deletions
|
@ -7,5 +7,7 @@ namespace Bit.App.Abstractions
|
||||||
string[] ProtectedFields { get; }
|
string[] ProtectedFields { get; }
|
||||||
|
|
||||||
Task<bool> ShowPasswordPromptAsync();
|
Task<bool> ShowPasswordPromptAsync();
|
||||||
|
|
||||||
|
Task<bool> Enabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,15 @@ namespace Bit.App
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (message.Command == "convertAccountToKeyConnector")
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
await Application.Current.MainPage.Navigation.PushModalAsync(
|
||||||
|
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public partial class LockPage : BaseContentPage
|
public partial class LockPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private readonly bool _autoPromptBiometric;
|
private readonly bool _autoPromptBiometric;
|
||||||
private readonly LockPageViewModel _vm;
|
private readonly LockPageViewModel _vm;
|
||||||
|
@ -21,7 +20,6 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true)
|
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_autoPromptBiometric = autoPromptBiometric;
|
_autoPromptBiometric = autoPromptBiometric;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
@ -130,6 +128,7 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
|
|
||||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace Bit.App.Pages
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IBiometricService _biometricService;
|
private readonly IBiometricService _biometricService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private string _email;
|
private string _email;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
|
@ -54,6 +55,7 @@ namespace Bit.App.Pages
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
PageTitle = AppResources.VerifyMasterPassword;
|
PageTitle = AppResources.VerifyMasterPassword;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
@ -124,6 +126,12 @@ namespace Bit.App.Pages
|
||||||
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
||||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
||||||
|
|
||||||
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
|
if (await _keyConnectorService.GetUsesKeyConnector() && !(BiometricLock || PinLock))
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.LogOutAsync();
|
||||||
|
}
|
||||||
_email = await _userService.GetEmailAsync();
|
_email = await _userService.GetEmailAsync();
|
||||||
var webVault = _environmentService.GetWebVaultUrl();
|
var webVault = _environmentService.GetWebVaultUrl();
|
||||||
if (string.IsNullOrWhiteSpace(webVault))
|
if (string.IsNullOrWhiteSpace(webVault))
|
||||||
|
|
|
@ -116,7 +116,14 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
RestoreAppOptionsFromCopy();
|
RestoreAppOptionsFromCopy();
|
||||||
await AppHelpers.ClearPreviousPage();
|
await AppHelpers.ClearPreviousPage();
|
||||||
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
|
{
|
||||||
|
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Application.Current.MainPage = new TabsPage(_appOptions, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml
Normal file
33
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?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"
|
||||||
|
x:Class="Bit.App.Pages.RemoveMasterPasswordPage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:RemoveMasterPasswordPageViewModel"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:RemoveMasterPasswordPageViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<StackLayout Spacing="20"
|
||||||
|
Padding="10, 5">
|
||||||
|
<StackLayout Spacing="18"
|
||||||
|
Padding="30">
|
||||||
|
<Label x:Name="_warningLabel"
|
||||||
|
HorizontalTextAlignment="Center"/>
|
||||||
|
<Label x:Name="_warningLabel2"
|
||||||
|
HorizontalTextAlignment="Center"/>
|
||||||
|
</StackLayout>
|
||||||
|
<StackLayout Spacing="5">
|
||||||
|
<Button Text="{u:I18n Continue}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Clicked="Continue_Clicked" />
|
||||||
|
<Button Text="{u:I18n LeaveOrganization}"
|
||||||
|
Clicked="LeaveOrg_Clicked" />
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
|
||||||
|
</pages:BaseContentPage>
|
58
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml.cs
Normal file
58
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml.cs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public partial class RemoveMasterPasswordPage : BaseContentPage
|
||||||
|
{
|
||||||
|
private readonly RemoveMasterPasswordPageViewModel _vm;
|
||||||
|
|
||||||
|
public Action NavigateAction { get; set; }
|
||||||
|
|
||||||
|
public RemoveMasterPasswordPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_vm = BindingContext as RemoveMasterPasswordPageViewModel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnAppearing()
|
||||||
|
{
|
||||||
|
await _vm.Init();
|
||||||
|
_warningLabel.Text = string.Format(AppResources.RemoveMasterPasswordWarning,
|
||||||
|
_vm.Organization.Name);
|
||||||
|
_warningLabel2.Text = AppResources.RemoveMasterPasswordWarning2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Continue_Clicked(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.MigrateAccount();
|
||||||
|
await Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void LeaveOrg_Clicked(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
var confirm = await DisplayAlert(AppResources.LeaveOrganization,
|
||||||
|
string.Format(AppResources.LeaveOrganizationName, _vm.Organization.Name),
|
||||||
|
AppResources.Yes, AppResources.No);
|
||||||
|
if (confirm)
|
||||||
|
{
|
||||||
|
await _vm.LeaveOrganization();
|
||||||
|
await Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnDisappearing()
|
||||||
|
{
|
||||||
|
NavigateAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs
Normal file
56
src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class RemoveMasterPasswordPageViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
private readonly ISyncService _syncService;
|
||||||
|
|
||||||
|
public Organization Organization;
|
||||||
|
|
||||||
|
public RemoveMasterPasswordPageViewModel()
|
||||||
|
{
|
||||||
|
PageTitle = AppResources.RemoveMasterPassword;
|
||||||
|
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
|
{
|
||||||
|
Organization = await _keyConnectorService.GetManagingOrganization();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MigrateAccount()
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
|
||||||
|
await _keyConnectorService.MigrateUser();
|
||||||
|
await _syncService.FullSyncAsync(true);
|
||||||
|
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LeaveOrganization()
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
|
||||||
|
await _apiService.PostLeaveOrganization(Organization.Id);
|
||||||
|
await _syncService.FullSyncAsync(true);
|
||||||
|
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,8 +25,9 @@
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<StackLayout Spacing="20">
|
<StackLayout>
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box"
|
||||||
|
Spacing="20">
|
||||||
<Frame
|
<Frame
|
||||||
IsVisible="{Binding DisablePrivateVaultPolicyEnabled}"
|
IsVisible="{Binding DisablePrivateVaultPolicyEnabled}"
|
||||||
Padding="10"
|
Padding="10"
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
StyleClass="text-muted, text-sm, text-bold"
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
</Frame>
|
</Frame>
|
||||||
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n FileFormat}"
|
Text="{u:I18n FileFormat}"
|
||||||
StyleClass="box-label" />
|
StyleClass="box-label" />
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Grid StyleClass="box-row">
|
<Grid StyleClass="box-row">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
@ -60,20 +62,30 @@
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Label
|
<Button x:Name="_requestOTP"
|
||||||
Text="{u:I18n MasterPassword}"
|
Text="{u:I18n RequestOTP}"
|
||||||
StyleClass="box-label"
|
Clicked="RequestOTP_Clicked"
|
||||||
|
HorizontalOptions="Fill"
|
||||||
|
VerticalOptions="End"
|
||||||
|
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}"
|
||||||
|
IsVisible="{Binding UseOTPVerification}"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding SecretName}"
|
||||||
|
StyleClass="box-label"
|
||||||
|
Grid.Row="1"
|
||||||
Grid.Column="0" />
|
Grid.Column="0" />
|
||||||
<controls:MonoEntry
|
<controls:MonoEntry
|
||||||
x:Name="_masterPassword"
|
x:Name="_secret"
|
||||||
Text="{Binding MasterPassword}"
|
Text="{Binding Secret}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
IsSpellCheckEnabled="False"
|
IsSpellCheckEnabled="False"
|
||||||
IsTextPredictionEnabled="False"
|
IsTextPredictionEnabled="False"
|
||||||
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||||
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}"
|
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}"
|
||||||
Grid.Row="1"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding ExportVaultCommand}" />
|
ReturnCommand="{Binding ExportVaultCommand}" />
|
||||||
|
@ -81,16 +93,16 @@
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
Text="{Binding ShowPasswordIcon}"
|
Text="{Binding ShowPasswordIcon}"
|
||||||
Command="{Binding TogglePasswordCommand}"
|
Command="{Binding TogglePasswordCommand}"
|
||||||
Grid.Row="0"
|
Grid.Row="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||||
|
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Label
|
<StackLayout StyleClass="box-row">
|
||||||
Text="{u:I18n ExportVaultMasterPasswordDescription}"
|
<Label
|
||||||
StyleClass="box-footer-label, box-footer-label-switch" />
|
Text="{Binding InstructionText}"
|
||||||
<StackLayout Spacing="20">
|
StyleClass="box-footer-label, box-footer-label-switch" />
|
||||||
<Button Text="{u:I18n ExportVault}"
|
<Button Text="{u:I18n ExportVault}"
|
||||||
Clicked="ExportVault_Clicked"
|
Clicked="ExportVault_Clicked"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Bit.App.Pages
|
||||||
_vm = BindingContext as ExportVaultPageViewModel;
|
_vm = BindingContext as ExportVaultPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_fileFormatPicker.ItemDisplayBinding = new Binding("Value");
|
_fileFormatPicker.ItemDisplayBinding = new Binding("Value");
|
||||||
MasterPasswordEntry = _masterPassword;
|
SecretEntry = _secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
|
@ -39,7 +39,7 @@ namespace Bit.App.Pages
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
RequestFocus(_masterPassword);
|
RequestFocus(_secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnDisappearing()
|
protected async override void OnDisappearing()
|
||||||
|
@ -48,7 +48,7 @@ namespace Bit.App.Pages
|
||||||
_broadcasterService.Unsubscribe(nameof(ExportVaultPage));
|
_broadcasterService.Unsubscribe(nameof(ExportVaultPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry MasterPasswordEntry { get; set; }
|
public Entry SecretEntry { get; set; }
|
||||||
|
|
||||||
private async void Close_Clicked(object sender, System.EventArgs e)
|
private async void Close_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
|
@ -66,6 +66,15 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void RequestOTP_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.RequestOTP();
|
||||||
|
_requestOTP.IsEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FileFormat_Changed(object sender, EventArgs e)
|
void FileFormat_Changed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_vm?.UpdateWarning();
|
_vm?.UpdateWarning();
|
||||||
|
|
|
@ -4,10 +4,8 @@ using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Microsoft.AppCenter.Crashes;
|
using Microsoft.AppCenter.Crashes;
|
||||||
|
@ -21,26 +19,33 @@ namespace Bit.App.Pages
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
private readonly ICryptoService _cryptoService;
|
|
||||||
private readonly IExportService _exportService;
|
private readonly IExportService _exportService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly IUserVerificationService _userVerificationService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
|
||||||
private int _fileFormatSelectedIndex;
|
private int _fileFormatSelectedIndex;
|
||||||
private string _exportWarningMessage;
|
private string _exportWarningMessage;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private string _masterPassword;
|
private string _secret;
|
||||||
private byte[] _exportResult;
|
private byte[] _exportResult;
|
||||||
private string _defaultFilename;
|
private string _defaultFilename;
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
|
private bool _useOTPVerification = false;
|
||||||
|
private string _secretName;
|
||||||
|
private string _instructionText;
|
||||||
|
|
||||||
public ExportVaultPageViewModel()
|
public ExportVaultPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
|
||||||
_exportService = ServiceContainer.Resolve<IExportService>("exportService");
|
_exportService = ServiceContainer.Resolve<IExportService>("exportService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
|
||||||
PageTitle = AppResources.ExportVault;
|
PageTitle = AppResources.ExportVault;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
@ -59,6 +64,18 @@ namespace Bit.App.Pages
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
||||||
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
||||||
|
UseOTPVerification = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
|
|
||||||
|
if (UseOTPVerification)
|
||||||
|
{
|
||||||
|
InstructionText = _i18nService.T("ExportVaultOTPDescription");
|
||||||
|
SecretName = _i18nService.T("VerificationCode");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstructionText = _i18nService.T("ExportVaultMasterPasswordDescription");
|
||||||
|
SecretName = _i18nService.T("MasterPassword");
|
||||||
|
}
|
||||||
|
|
||||||
UpdateWarning();
|
UpdateWarning();
|
||||||
}
|
}
|
||||||
|
@ -94,10 +111,28 @@ namespace Bit.App.Pages
|
||||||
additionalPropertyNames: new string[] {nameof(ShowPasswordIcon)});
|
additionalPropertyNames: new string[] {nameof(ShowPasswordIcon)});
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MasterPassword
|
public bool UseOTPVerification
|
||||||
{
|
{
|
||||||
get => _masterPassword;
|
get => _useOTPVerification;
|
||||||
set => SetProperty(ref _masterPassword, value);
|
set => SetProperty(ref _useOTPVerification, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Secret
|
||||||
|
{
|
||||||
|
get => _secret;
|
||||||
|
set => SetProperty(ref _secret, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SecretName
|
||||||
|
{
|
||||||
|
get => _secretName;
|
||||||
|
set => SetProperty(ref _secretName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string InstructionText
|
||||||
|
{
|
||||||
|
get => _instructionText;
|
||||||
|
set => SetProperty(ref _instructionText, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
|
@ -107,27 +142,13 @@ namespace Bit.App.Pages
|
||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
(Page as ExportVaultPage).MasterPasswordEntry.Focus();
|
(Page as ExportVaultPage).SecretEntry.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command ExportVaultCommand { get; }
|
public Command ExportVaultCommand { get; }
|
||||||
|
|
||||||
public async Task ExportVaultAsync()
|
public async Task ExportVaultAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_masterPassword))
|
|
||||||
{
|
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("InvalidMasterPassword"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(_masterPassword, null);
|
|
||||||
MasterPassword = string.Empty;
|
|
||||||
if (!passwordValid)
|
|
||||||
{
|
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("InvalidMasterPassword"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool userConfirmedExport = await _platformUtilsService.ShowDialogAsync(ExportWarningMessage,
|
bool userConfirmedExport = await _platformUtilsService.ShowDialogAsync(ExportWarningMessage,
|
||||||
_i18nService.T("ExportVaultConfirmationTitle"), _i18nService.T("ExportVault"), _i18nService.T("Cancel"));
|
_i18nService.T("ExportVaultConfirmationTitle"), _i18nService.T("ExportVault"), _i18nService.T("Cancel"));
|
||||||
|
|
||||||
|
@ -136,6 +157,16 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var verificationType = await _keyConnectorService.GetUsesKeyConnector()
|
||||||
|
? VerificationType.OTP
|
||||||
|
: VerificationType.MasterPassword;
|
||||||
|
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Secret = string.Empty;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = await _exportService.GetExport(FileFormatOptions[FileFormatSelectedIndex].Key);
|
var data = await _exportService.GetExport(FileFormatOptions[FileFormatSelectedIndex].Key);
|
||||||
|
@ -162,6 +193,11 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RequestOTP()
|
||||||
|
{
|
||||||
|
await _apiService.PostAccountRequestOTP();
|
||||||
|
}
|
||||||
|
|
||||||
public async void SaveFileSelected(string contentUri, string filename)
|
public async void SaveFileSelected(string contentUri, string filename)
|
||||||
{
|
{
|
||||||
if (_deviceActionService.SaveFile(_exportResult, null, filename ?? _defaultFilename, contentUri))
|
if (_deviceActionService.SaveFile(_exportResult, null, filename ?? _defaultFilename, contentUri))
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace Bit.App.Pages
|
||||||
private readonly IBiometricService _biometricService;
|
private readonly IBiometricService _biometricService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private const int CustomVaultTimeoutValue = -100;
|
private const int CustomVaultTimeoutValue = -100;
|
||||||
|
|
||||||
|
@ -36,6 +37,8 @@ namespace Bit.App.Pages
|
||||||
private string _lastSyncDate;
|
private string _lastSyncDate;
|
||||||
private string _vaultTimeoutDisplayValue;
|
private string _vaultTimeoutDisplayValue;
|
||||||
private string _vaultTimeoutActionDisplayValue;
|
private string _vaultTimeoutActionDisplayValue;
|
||||||
|
private bool _showChangeMasterPassword;
|
||||||
|
|
||||||
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
||||||
new List<KeyValuePair<string, int?>>
|
new List<KeyValuePair<string, int?>>
|
||||||
{
|
{
|
||||||
|
@ -74,6 +77,7 @@ namespace Bit.App.Pages
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
||||||
PageTitle = AppResources.Settings;
|
PageTitle = AppResources.Settings;
|
||||||
|
@ -116,6 +120,9 @@ namespace Bit.App.Pages
|
||||||
_vaultTimeoutDisplayValue = AppResources.Custom;
|
_vaultTimeoutDisplayValue = AppResources.Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
||||||
|
!await _keyConnectorService.GetUsesKeyConnector();
|
||||||
|
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +467,7 @@ namespace Bit.App.Pages
|
||||||
new SettingsPageListItem { Name = AppResources.FingerprintPhrase },
|
new SettingsPageListItem { Name = AppResources.FingerprintPhrase },
|
||||||
new SettingsPageListItem { Name = AppResources.LogOut }
|
new SettingsPageListItem { Name = AppResources.LogOut }
|
||||||
};
|
};
|
||||||
if (IncludeLinksWithSubscriptionInfo())
|
if (_showChangeMasterPassword)
|
||||||
{
|
{
|
||||||
accountItems.Insert(0, new SettingsPageListItem { Name = AppResources.ChangeMasterPassword });
|
accountItems.Insert(0, new SettingsPageListItem { Name = AppResources.ChangeMasterPassword });
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace Bit.App.Pages
|
||||||
public class TabsPage : TabbedPage
|
public class TabsPage : TabbedPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private NavigationPage _groupingsPage;
|
private NavigationPage _groupingsPage;
|
||||||
private NavigationPage _sendGroupingsPage;
|
private NavigationPage _sendGroupingsPage;
|
||||||
|
@ -18,6 +19,7 @@ namespace Bit.App.Pages
|
||||||
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
|
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
|
||||||
{
|
{
|
||||||
|
@ -72,6 +74,15 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async void OnAppearing()
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
if (await _keyConnectorService.UserNeedsMigration())
|
||||||
|
{
|
||||||
|
_messagingService.Send("convertAccountToKeyConnector");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetToVaultPage()
|
public void ResetToVaultPage()
|
||||||
{
|
{
|
||||||
CurrentPage = _groupingsPage;
|
CurrentPage = _groupingsPage;
|
||||||
|
|
|
@ -556,7 +556,7 @@
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout x:Name="_passwordPrompt" StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n PasswordPrompt}"
|
Text="{u:I18n PasswordPrompt}"
|
||||||
StyleClass="box-label-regular" />
|
StyleClass="box-label-regular" />
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace Bit.App.Pages
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private AddEditPageViewModel _vm;
|
private AddEditPageViewModel _vm;
|
||||||
private bool _fromAutofill;
|
private bool _fromAutofill;
|
||||||
|
@ -40,6 +41,8 @@ namespace Bit.App.Pages
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_fromAutofill = fromAutofill;
|
_fromAutofill = fromAutofill;
|
||||||
FromAutofillFramework = _appOptions?.FromAutofillFramework ?? false;
|
FromAutofillFramework = _appOptions?.FromAutofillFramework ?? false;
|
||||||
|
@ -171,6 +174,8 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
||||||
});
|
});
|
||||||
|
// Hide password reprompt option if using key connector
|
||||||
|
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnector();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
|
|
48
src/App/Resources/AppResources.Designer.cs
generated
48
src/App/Resources/AppResources.Designer.cs
generated
|
@ -2837,6 +2837,12 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ExportVaultOTPDescription {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ExportVaultOTPDescription", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string ExportVaultWarning {
|
public static string ExportVaultWarning {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ExportVaultWarning", resourceCulture);
|
return ResourceManager.GetString("ExportVaultWarning", resourceCulture);
|
||||||
|
@ -3599,6 +3605,36 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string RemoveMasterPassword {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveMasterPassword", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RemoveMasterPasswordWarning {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveMasterPasswordWarning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RemoveMasterPasswordWarning2 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveMasterPasswordWarning2", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string LeaveOrganization {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LeaveOrganization", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string LeaveOrganizationName {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LeaveOrganizationName", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string Fido2Title {
|
public static string Fido2Title {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("Fido2Title", resourceCulture);
|
return ResourceManager.GetString("Fido2Title", resourceCulture);
|
||||||
|
@ -3658,5 +3694,17 @@ namespace Bit.App.Resources {
|
||||||
return ResourceManager.GetString("DisablePersonalVaultExportPolicyInEffect", resourceCulture);
|
return ResourceManager.GetString("DisablePersonalVaultExportPolicyInEffect", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string InvalidVerificationCode {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("InvalidVerificationCode", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RequestOTP {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RequestOTP", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1610,6 +1610,9 @@
|
||||||
</data>
|
</data>
|
||||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||||
<value>Enter your master password to export your vault data.</value>
|
<value>Enter your master password to export your vault data.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExportVaultOTPDescription" xml:space="preserve">
|
||||||
|
<value>Enter the verification code to export your vault data.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExportVaultWarning" xml:space="preserve">
|
<data name="ExportVaultWarning" xml:space="preserve">
|
||||||
<value>This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it.</value>
|
<value>This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it.</value>
|
||||||
|
@ -2033,6 +2036,21 @@
|
||||||
<data name="UpdatePasswordError" xml:space="preserve">
|
<data name="UpdatePasswordError" xml:space="preserve">
|
||||||
<value>Currently unable to update password</value>
|
<value>Currently unable to update password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||||
|
<value>Remove Master Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||||
|
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||||
|
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganization" xml:space="preserve">
|
||||||
|
<value>Leave Organization</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||||
|
<value>Leave {0}?</value>
|
||||||
|
</data>
|
||||||
<data name="Fido2Title" xml:space="preserve">
|
<data name="Fido2Title" xml:space="preserve">
|
||||||
<value>FIDO2 WebAuthn</value>
|
<value>FIDO2 WebAuthn</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -2063,4 +2081,10 @@
|
||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||||
|
<value>Invalid Verification Code.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequestOTP" xml:space="preserve">
|
||||||
|
<value>Request one-time password</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Bit.Core.Abstractions;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using System;
|
using System;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
|
@ -21,6 +22,11 @@ namespace Bit.App.Services
|
||||||
|
|
||||||
public async Task<bool> ShowPasswordPromptAsync()
|
public async Task<bool> ShowPasswordPromptAsync()
|
||||||
{
|
{
|
||||||
|
if (!await Enabled())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Func<string, Task<bool>> validator = async (string password) =>
|
Func<string, Task<bool>> validator = async (string password) =>
|
||||||
{
|
{
|
||||||
// Assume user has canceled.
|
// Assume user has canceled.
|
||||||
|
@ -34,5 +40,11 @@ namespace Bit.App.Services
|
||||||
|
|
||||||
return await _platformUtilsService.ShowPasswordDialogAsync(AppResources.PasswordConfirmation, AppResources.PasswordConfirmationDesc, validator);
|
return await _platformUtilsService.ShowPasswordDialogAsync(AppResources.PasswordConfirmation, AppResources.PasswordConfirmationDesc, validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Enabled()
|
||||||
|
{
|
||||||
|
var keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
return !await keyConnectorService.GetUsesKeyConnector();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace Bit.Core.Abstractions
|
||||||
Task<SyncResponse> GetSyncAsync();
|
Task<SyncResponse> GetSyncAsync();
|
||||||
Task PostAccountKeysAsync(KeysRequest request);
|
Task PostAccountKeysAsync(KeysRequest request);
|
||||||
Task PostAccountVerifyPasswordAsync(PasswordVerificationRequest request);
|
Task PostAccountVerifyPasswordAsync(PasswordVerificationRequest request);
|
||||||
|
Task PostAccountRequestOTP();
|
||||||
|
Task PostAccountVerifyOTPAsync(VerifyOTPRequest request);
|
||||||
Task<CipherResponse> PostCipherAsync(CipherRequest request);
|
Task<CipherResponse> PostCipherAsync(CipherRequest request);
|
||||||
Task<CipherResponse> PostCipherCreateAsync(CipherCreateRequest request);
|
Task<CipherResponse> PostCipherCreateAsync(CipherCreateRequest request);
|
||||||
Task<FolderResponse> PostFolderAsync(FolderRequest request);
|
Task<FolderResponse> PostFolderAsync(FolderRequest request);
|
||||||
|
@ -63,6 +65,11 @@ namespace Bit.Core.Abstractions
|
||||||
Task<OrganizationAutoEnrollStatusResponse> GetOrganizationAutoEnrollStatusAsync(string identifier);
|
Task<OrganizationAutoEnrollStatusResponse> GetOrganizationAutoEnrollStatusAsync(string identifier);
|
||||||
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
||||||
OrganizationUserResetPasswordEnrollmentRequest request);
|
OrganizationUserResetPasswordEnrollmentRequest request);
|
||||||
|
Task<KeyConnectorUserKeyResponse> GetUserKeyFromKeyConnector(string keyConnectorUrl);
|
||||||
|
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
||||||
|
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
|
||||||
|
Task PostConvertToKeyConnector();
|
||||||
|
Task PostLeaveOrganization(string id);
|
||||||
|
|
||||||
Task<SendResponse> GetSendAsync(string id);
|
Task<SendResponse> GetSendAsync(string id);
|
||||||
Task<SendResponse> PostSendAsync(SendRequest request);
|
Task<SendResponse> PostSendAsync(SendRequest request);
|
||||||
|
|
16
src/Core/Abstractions/IKeyConnectorService.cs
Normal file
16
src/Core/Abstractions/IKeyConnectorService.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
|
||||||
|
namespace Bit.Core.Abstractions
|
||||||
|
{
|
||||||
|
public interface IKeyConnectorService
|
||||||
|
{
|
||||||
|
Task SetUsesKeyConnector(bool usesKeyConnector);
|
||||||
|
Task<bool> GetUsesKeyConnector();
|
||||||
|
Task<bool> UserNeedsMigration();
|
||||||
|
Task MigrateUser();
|
||||||
|
Task GetAndSetKey(string url);
|
||||||
|
Task<Organization> GetManagingOrganization();
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ namespace Bit.Core.Abstractions
|
||||||
string GetIssuer();
|
string GetIssuer();
|
||||||
string GetName();
|
string GetName();
|
||||||
bool GetPremium();
|
bool GetPremium();
|
||||||
|
bool GetIsExternal();
|
||||||
Task<string> GetRefreshTokenAsync();
|
Task<string> GetRefreshTokenAsync();
|
||||||
Task<string> GetTokenAsync();
|
Task<string> GetTokenAsync();
|
||||||
Task ToggleTokensAsync();
|
Task ToggleTokensAsync();
|
||||||
|
|
10
src/Core/Abstractions/IUserVerificationService.cs
Normal file
10
src/Core/Abstractions/IUserVerificationService.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Abstractions
|
||||||
|
{
|
||||||
|
public interface IUserVerificationService
|
||||||
|
{
|
||||||
|
Task<bool> VerifyUser(string secret, VerificationType verificationType);
|
||||||
|
}
|
||||||
|
}
|
8
src/Core/Enums/VerificationType.cs
Normal file
8
src/Core/Enums/VerificationType.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Bit.Core.Enums
|
||||||
|
{
|
||||||
|
public enum VerificationType
|
||||||
|
{
|
||||||
|
MasterPassword = 0,
|
||||||
|
OTP = 1,
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,8 @@ namespace Bit.Core.Models.Data
|
||||||
MaxStorageGb = response.MaxStorageGb;
|
MaxStorageGb = response.MaxStorageGb;
|
||||||
Permissions = response.Permissions ?? new Permissions();
|
Permissions = response.Permissions ?? new Permissions();
|
||||||
Identifier = response.Identifier;
|
Identifier = response.Identifier;
|
||||||
|
UsesKeyConnector = response.UsesKeyConnector;
|
||||||
|
KeyConnectorUrl = response.KeyConnectorUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
@ -50,5 +52,7 @@ namespace Bit.Core.Models.Data
|
||||||
public short? MaxStorageGb { get; set; }
|
public short? MaxStorageGb { get; set; }
|
||||||
public Permissions Permissions { get; set; } = new Permissions();
|
public Permissions Permissions { get; set; } = new Permissions();
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public string KeyConnectorUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace Bit.Core.Models.Domain
|
||||||
MaxStorageGb = obj.MaxStorageGb;
|
MaxStorageGb = obj.MaxStorageGb;
|
||||||
Permissions = obj.Permissions ?? new Permissions();
|
Permissions = obj.Permissions ?? new Permissions();
|
||||||
Identifier = obj.Identifier;
|
Identifier = obj.Identifier;
|
||||||
|
UsesKeyConnector = obj.UsesKeyConnector;
|
||||||
|
KeyConnectorUrl = obj.KeyConnectorUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
@ -50,6 +52,8 @@ namespace Bit.Core.Models.Domain
|
||||||
public short? MaxStorageGb { get; set; }
|
public short? MaxStorageGb { get; set; }
|
||||||
public Permissions Permissions { get; set; } = new Permissions();
|
public Permissions Permissions { get; set; } = new Permissions();
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public string KeyConnectorUrl { get; set; }
|
||||||
|
|
||||||
public bool CanAccess
|
public bool CanAccess
|
||||||
{
|
{
|
||||||
|
|
13
src/Core/Models/Request/KeyConnectorUserKeyRequest.cs
Normal file
13
src/Core/Models/Request/KeyConnectorUserKeyRequest.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
namespace Bit.Core.Models.Request
|
||||||
|
{
|
||||||
|
public class KeyConnectorUserKeyRequest
|
||||||
|
{
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
public KeyConnectorUserKeyRequest(string key)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/Core/Models/Request/SetKeyConnectorKeyRequest.cs
Normal file
24
src/Core/Models/Request/SetKeyConnectorKeyRequest.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Request
|
||||||
|
{
|
||||||
|
public class SetKeyConnectorKeyRequest
|
||||||
|
{
|
||||||
|
public string Key { get; set; }
|
||||||
|
public KeysRequest Keys { get; set; }
|
||||||
|
public KdfType Kdf { get; set; }
|
||||||
|
public int? KdfIterations { get; set; }
|
||||||
|
public string OrgIdentifier { get; set; }
|
||||||
|
|
||||||
|
public SetKeyConnectorKeyRequest(string key, KeysRequest keys,
|
||||||
|
KdfType kdf, int? kdfIterations, string orgIdentifier)
|
||||||
|
{
|
||||||
|
this.Key = key;
|
||||||
|
this.Keys = keys;
|
||||||
|
this.Kdf = kdf;
|
||||||
|
this.KdfIterations = kdfIterations;
|
||||||
|
this.OrgIdentifier = orgIdentifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/Core/Models/Request/VerifyOTPRequest.cs
Normal file
13
src/Core/Models/Request/VerifyOTPRequest.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
namespace Bit.Core.Models.Request
|
||||||
|
{
|
||||||
|
public class VerifyOTPRequest
|
||||||
|
{
|
||||||
|
public string OTP;
|
||||||
|
|
||||||
|
public VerifyOTPRequest(string otp)
|
||||||
|
{
|
||||||
|
OTP = otp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,5 +21,6 @@ namespace Bit.Core.Models.Response
|
||||||
public KdfType Kdf { get; set; }
|
public KdfType Kdf { get; set; }
|
||||||
public int? KdfIterations { get; set; }
|
public int? KdfIterations { get; set; }
|
||||||
public bool ForcePasswordReset { get; set; }
|
public bool ForcePasswordReset { get; set; }
|
||||||
|
public string KeyConnectorUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
src/Core/Models/Response/KeyConnectorUserKeyResponse.cs
Normal file
9
src/Core/Models/Response/KeyConnectorUserKeyResponse.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Response
|
||||||
|
{
|
||||||
|
public class KeyConnectorUserKeyResponse
|
||||||
|
{
|
||||||
|
public string Key { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,5 +25,8 @@ namespace Bit.Core.Models.Response
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public Permissions Permissions { get; set; } = new Permissions();
|
public Permissions Permissions { get; set; } = new Permissions();
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public string KeyConnectorUrl { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,6 @@ namespace Bit.Core.Models.Response
|
||||||
public string SecurityStamp { get; set; }
|
public string SecurityStamp { get; set; }
|
||||||
public bool ForcePasswordReset { get; set; }
|
public bool ForcePasswordReset { get; set; }
|
||||||
public List<ProfileOrganizationResponse> Organizations { get; set; }
|
public List<ProfileOrganizationResponse> Organizations { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,12 +178,33 @@ namespace Bit.Core.Services
|
||||||
true, false);
|
true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task PostAccountRequestOTP()
|
||||||
|
{
|
||||||
|
return SendAsync<object, object>(HttpMethod.Post, "/accounts/request-otp", null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task PostAccountVerifyOTPAsync(VerifyOTPRequest request)
|
||||||
|
{
|
||||||
|
return SendAsync<VerifyOTPRequest, object>(HttpMethod.Post, "/accounts/verify-otp", request,
|
||||||
|
true, false);
|
||||||
|
}
|
||||||
|
|
||||||
public Task PutUpdateTempPasswordAsync(UpdateTempPasswordRequest request)
|
public Task PutUpdateTempPasswordAsync(UpdateTempPasswordRequest request)
|
||||||
{
|
{
|
||||||
return SendAsync<UpdateTempPasswordRequest, object>(HttpMethod.Put, "/accounts/update-temp-password",
|
return SendAsync<UpdateTempPasswordRequest, object>(HttpMethod.Put, "/accounts/update-temp-password",
|
||||||
request, true, false);
|
request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task PostConvertToKeyConnector()
|
||||||
|
{
|
||||||
|
return SendAsync<object, object>(HttpMethod.Post, "/accounts/convert-to-key-connector", null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request)
|
||||||
|
{
|
||||||
|
return SendAsync<SetKeyConnectorKeyRequest>(HttpMethod.Post, "/accounts/set-key-connector-key", request, true);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Folder APIs
|
#region Folder APIs
|
||||||
|
@ -423,6 +444,11 @@ namespace Bit.Core.Services
|
||||||
$"/organizations/{identifier}/auto-enroll-status", null, true, true);
|
$"/organizations/{identifier}/auto-enroll-status", null, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task PostLeaveOrganization(string id)
|
||||||
|
{
|
||||||
|
return SendAsync<object, object>(HttpMethod.Post, $"/organizations/{id}/leave", null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Organization User APIs
|
#region Organization User APIs
|
||||||
|
@ -436,6 +462,70 @@ namespace Bit.Core.Services
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Key Connector
|
||||||
|
|
||||||
|
public async Task<KeyConnectorUserKeyResponse> GetUserKeyFromKeyConnector(string keyConnectorUrl)
|
||||||
|
{
|
||||||
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
|
{
|
||||||
|
var authHeader = await GetActiveBearerTokenAsync();
|
||||||
|
|
||||||
|
requestMessage.Version = new Version(1, 0);
|
||||||
|
requestMessage.Method = HttpMethod.Get;
|
||||||
|
requestMessage.RequestUri = new Uri(string.Concat(keyConnectorUrl, "/user-keys"));
|
||||||
|
requestMessage.Headers.Add("Authorization", string.Concat("Bearer ", authHeader));
|
||||||
|
|
||||||
|
HttpResponseMessage response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = await _httpClient.SendAsync(requestMessage);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ApiException(HandleWebError(e));
|
||||||
|
}
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var error = await HandleErrorAsync(response, false, true);
|
||||||
|
throw new ApiException(error);
|
||||||
|
}
|
||||||
|
var responseJsonString = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<KeyConnectorUserKeyResponse>(responseJsonString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request)
|
||||||
|
{
|
||||||
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
|
{
|
||||||
|
var authHeader = await GetActiveBearerTokenAsync();
|
||||||
|
|
||||||
|
requestMessage.Version = new Version(1, 0);
|
||||||
|
requestMessage.Method = HttpMethod.Post;
|
||||||
|
requestMessage.RequestUri = new Uri(string.Concat(keyConnectorUrl, "/user-keys"));
|
||||||
|
requestMessage.Headers.Add("Authorization", string.Concat("Bearer ", authHeader));
|
||||||
|
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(request, _jsonSettings),
|
||||||
|
Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
HttpResponseMessage response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = await _httpClient.SendAsync(requestMessage);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ApiException(HandleWebError(e));
|
||||||
|
}
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var error = await HandleErrorAsync(response, false, true);
|
||||||
|
throw new ApiException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
|
|
||||||
public async Task<string> GetActiveBearerTokenAsync()
|
public async Task<string> GetActiveBearerTokenAsync()
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace Bit.Core.Services
|
||||||
public class AuthService : IAuthService
|
public class AuthService : IAuthService
|
||||||
{
|
{
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
|
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly ITokenService _tokenService;
|
private readonly ITokenService _tokenService;
|
||||||
|
@ -20,12 +21,14 @@ namespace Bit.Core.Services
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly bool _setCryptoKeys;
|
private readonly bool _setCryptoKeys;
|
||||||
|
|
||||||
private SymmetricCryptoKey _key;
|
private SymmetricCryptoKey _key;
|
||||||
|
|
||||||
public AuthService(
|
public AuthService(
|
||||||
ICryptoService cryptoService,
|
ICryptoService cryptoService,
|
||||||
|
ICryptoFunctionService cryptoFunctionService,
|
||||||
IApiService apiService,
|
IApiService apiService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
ITokenService tokenService,
|
ITokenService tokenService,
|
||||||
|
@ -34,9 +37,11 @@ namespace Bit.Core.Services
|
||||||
IPlatformUtilsService platformUtilsService,
|
IPlatformUtilsService platformUtilsService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IVaultTimeoutService vaultTimeoutService,
|
IVaultTimeoutService vaultTimeoutService,
|
||||||
|
IKeyConnectorService keyConnectorService,
|
||||||
bool setCryptoKeys = true)
|
bool setCryptoKeys = true)
|
||||||
{
|
{
|
||||||
_cryptoService = cryptoService;
|
_cryptoService = cryptoService;
|
||||||
|
_cryptoFunctionService = cryptoFunctionService;
|
||||||
_apiService = apiService;
|
_apiService = apiService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_tokenService = tokenService;
|
_tokenService = tokenService;
|
||||||
|
@ -45,6 +50,7 @@ namespace Bit.Core.Services
|
||||||
_platformUtilsService = platformUtilsService;
|
_platformUtilsService = platformUtilsService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_vaultTimeoutService = vaultTimeoutService;
|
_vaultTimeoutService = vaultTimeoutService;
|
||||||
|
_keyConnectorService = keyConnectorService;
|
||||||
_setCryptoKeys = setCryptoKeys;
|
_setCryptoKeys = setCryptoKeys;
|
||||||
|
|
||||||
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||||
|
@ -275,7 +281,7 @@ namespace Bit.Core.Services
|
||||||
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
||||||
string code, string codeVerifier, string redirectUrl, SymmetricCryptoKey key,
|
string code, string codeVerifier, string redirectUrl, SymmetricCryptoKey key,
|
||||||
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
||||||
string captchaToken = null)
|
string captchaToken = null, string orgId = null)
|
||||||
{
|
{
|
||||||
var storedTwoFactorToken = await _tokenService.GetTwoFactorTokenAsync(email);
|
var storedTwoFactorToken = await _tokenService.GetTwoFactorTokenAsync(email);
|
||||||
var appId = await _appIdService.GetAppIdAsync();
|
var appId = await _appIdService.GetAppIdAsync();
|
||||||
|
@ -353,27 +359,75 @@ namespace Bit.Core.Services
|
||||||
tokenResponse.Kdf, tokenResponse.KdfIterations);
|
tokenResponse.Kdf, tokenResponse.KdfIterations);
|
||||||
if (_setCryptoKeys)
|
if (_setCryptoKeys)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
if (key != null)
|
||||||
await _cryptoService.SetKeyHashAsync(localHashedPassword);
|
|
||||||
await _cryptoService.SetEncKeyAsync(tokenResponse.Key);
|
|
||||||
|
|
||||||
// User doesn't have a key pair yet (old account), let's generate one for them.
|
|
||||||
if (tokenResponse.PrivateKey == null)
|
|
||||||
{
|
{
|
||||||
try
|
await _cryptoService.SetKeyAsync(key);
|
||||||
{
|
}
|
||||||
var keyPair = await _cryptoService.MakeKeyPairAsync();
|
|
||||||
await _apiService.PostAccountKeysAsync(new KeysRequest
|
if (localHashedPassword != null)
|
||||||
{
|
{
|
||||||
PublicKey = keyPair.Item1,
|
await _cryptoService.SetKeyHashAsync(localHashedPassword);
|
||||||
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
}
|
||||||
});
|
|
||||||
tokenResponse.PrivateKey = keyPair.Item2.EncryptedString;
|
if (code == null || tokenResponse.Key != null)
|
||||||
}
|
{
|
||||||
catch { }
|
if (tokenResponse.KeyConnectorUrl != null)
|
||||||
|
{
|
||||||
|
await _keyConnectorService.GetAndSetKey(tokenResponse.KeyConnectorUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cryptoService.SetEncKeyAsync(tokenResponse.Key);
|
||||||
|
|
||||||
|
// User doesn't have a key pair yet (old account), let's generate one for them.
|
||||||
|
if (tokenResponse.PrivateKey == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var keyPair = await _cryptoService.MakeKeyPairAsync();
|
||||||
|
await _apiService.PostAccountKeysAsync(new KeysRequest
|
||||||
|
{
|
||||||
|
PublicKey = keyPair.Item1,
|
||||||
|
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
||||||
|
});
|
||||||
|
tokenResponse.PrivateKey = keyPair.Item2.EncryptedString;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||||
|
}
|
||||||
|
else if (tokenResponse.KeyConnectorUrl != null)
|
||||||
|
{
|
||||||
|
// SSO Key Connector Onboarding
|
||||||
|
var password = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||||
|
var k = await _cryptoService.MakeKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.Kdf, tokenResponse.KdfIterations);
|
||||||
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(k.EncKeyB64);
|
||||||
|
await _cryptoService.SetKeyAsync(k);
|
||||||
|
|
||||||
|
var encKey = await _cryptoService.MakeEncKeyAsync(k);
|
||||||
|
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||||
|
var keyPair = await _cryptoService.MakeKeyPairAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _apiService.PostUserKeyToKeyConnector(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception("Unable to reach Key Connector", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = new KeysRequest
|
||||||
|
{
|
||||||
|
PublicKey = keyPair.Item1,
|
||||||
|
EncryptedPrivateKey = keyPair.Item2.EncryptedString
|
||||||
|
};
|
||||||
|
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||||
|
encKey.Item2.EncryptedString, keys, tokenResponse.Kdf, tokenResponse.KdfIterations, orgId
|
||||||
|
);
|
||||||
|
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_vaultTimeoutService.BiometricLocked = false;
|
_vaultTimeoutService.BiometricLocked = false;
|
||||||
|
|
98
src/Core/Services/KeyConnectorService.cs
Normal file
98
src/Core/Services/KeyConnectorService.cs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class KeyConnectorService : IKeyConnectorService
|
||||||
|
{
|
||||||
|
private const string Keys_UsesKeyConnector = "usesKeyConnector";
|
||||||
|
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
private readonly ICryptoService _cryptoService;
|
||||||
|
private readonly IStorageService _storageService;
|
||||||
|
private readonly ITokenService _tokenService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
|
||||||
|
private bool? _usesKeyConnector;
|
||||||
|
|
||||||
|
public KeyConnectorService(IUserService userService, ICryptoService cryptoService,
|
||||||
|
IStorageService storageService, ITokenService tokenService, IApiService apiService)
|
||||||
|
{
|
||||||
|
_userService = userService;
|
||||||
|
_cryptoService = cryptoService;
|
||||||
|
_storageService = storageService;
|
||||||
|
_tokenService = tokenService;
|
||||||
|
_apiService = apiService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GetAndSetKey(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userKeyResponse = await _apiService.GetUserKeyFromKeyConnector(url);
|
||||||
|
var keyArr = Convert.FromBase64String(userKeyResponse.Key);
|
||||||
|
var k = new SymmetricCryptoKey(keyArr);
|
||||||
|
await _cryptoService.SetKeyAsync(k);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception("Unable to reach Key Connector", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUsesKeyConnector(bool usesKeyConnector)
|
||||||
|
{
|
||||||
|
_usesKeyConnector = usesKeyConnector;
|
||||||
|
await _storageService.SaveAsync(Keys_UsesKeyConnector, usesKeyConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> GetUsesKeyConnector()
|
||||||
|
{
|
||||||
|
if (!_usesKeyConnector.HasValue)
|
||||||
|
{
|
||||||
|
_usesKeyConnector = await _storageService.GetAsync<bool>(Keys_UsesKeyConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _usesKeyConnector.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Organization> GetManagingOrganization()
|
||||||
|
{
|
||||||
|
var orgs = await _userService.GetAllOrganizationAsync();
|
||||||
|
return orgs.Find(o =>
|
||||||
|
o.UsesKeyConnector &&
|
||||||
|
!o.IsAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MigrateUser()
|
||||||
|
{
|
||||||
|
var organization = await GetManagingOrganization();
|
||||||
|
var key = await _cryptoService.GetKeyAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(key.EncKeyB64);
|
||||||
|
await _apiService.PostUserKeyToKeyConnector(organization.KeyConnectorUrl, keyConnectorRequest);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception("Unable to reach Key Connector", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _apiService.PostConvertToKeyConnector();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UserNeedsMigration()
|
||||||
|
{
|
||||||
|
var loggedInUsingSso = _tokenService.GetIsExternal();
|
||||||
|
var requiredByOrganization = await GetManagingOrganization() != null;
|
||||||
|
var userIsNotUsingKeyConnector = !await GetUsesKeyConnector();
|
||||||
|
|
||||||
|
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ namespace Bit.Core.Services
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ISendService _sendService;
|
private readonly ISendService _sendService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly Func<bool, Task> _logoutCallbackAsync;
|
private readonly Func<bool, Task> _logoutCallbackAsync;
|
||||||
|
|
||||||
public SyncService(
|
public SyncService(
|
||||||
|
@ -39,6 +40,7 @@ namespace Bit.Core.Services
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
ISendService sendService,
|
ISendService sendService,
|
||||||
|
IKeyConnectorService keyConnectorService,
|
||||||
Func<bool, Task> logoutCallbackAsync)
|
Func<bool, Task> logoutCallbackAsync)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
@ -52,6 +54,7 @@ namespace Bit.Core.Services
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
_sendService = sendService;
|
_sendService = sendService;
|
||||||
|
_keyConnectorService = keyConnectorService;
|
||||||
_logoutCallbackAsync = logoutCallbackAsync;
|
_logoutCallbackAsync = logoutCallbackAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +332,7 @@ namespace Bit.Core.Services
|
||||||
await _userService.ReplaceOrganizationsAsync(organizations);
|
await _userService.ReplaceOrganizationsAsync(organizations);
|
||||||
await _userService.SetEmailVerifiedAsync(response.EmailVerified);
|
await _userService.SetEmailVerifiedAsync(response.EmailVerified);
|
||||||
await _userService.SetForcePasswordReset(response.ForcePasswordReset);
|
await _userService.SetForcePasswordReset(response.ForcePasswordReset);
|
||||||
|
await _keyConnectorService.SetUsesKeyConnector(response.UsesKeyConnector);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SyncFoldersAsync(string userId, List<FolderResponse> response)
|
private async Task SyncFoldersAsync(string userId, List<FolderResponse> response)
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
|
@ -230,6 +231,16 @@ namespace Bit.Core.Services
|
||||||
return decoded["iss"].Value<string>();
|
return decoded["iss"].Value<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool GetIsExternal()
|
||||||
|
{
|
||||||
|
var decoded = DecodeToken();
|
||||||
|
if (decoded?["amr"] == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return decoded["amr"].Value<JArray>().Any(t => t.Value<string>() == "external");
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> SkipTokenStorage()
|
private async Task<bool> SkipTokenStorage()
|
||||||
{
|
{
|
||||||
var timeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
var timeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||||
|
|
69
src/Core/Services/UserVerificationService.cs
Normal file
69
src/Core/Services/UserVerificationService.cs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
using System;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class UserVerificationService : IUserVerificationService
|
||||||
|
{
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
private readonly II18nService _i18nService;
|
||||||
|
private readonly ICryptoService _cryptoService;
|
||||||
|
|
||||||
|
public UserVerificationService(IApiService apiService, IPlatformUtilsService platformUtilsService,
|
||||||
|
II18nService i18nService, ICryptoService cryptoService)
|
||||||
|
{
|
||||||
|
_apiService = apiService;
|
||||||
|
_platformUtilsService = platformUtilsService;
|
||||||
|
_i18nService = i18nService;
|
||||||
|
_cryptoService = cryptoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
async public Task<bool> VerifyUser(string secret, VerificationType verificationType)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(secret))
|
||||||
|
{
|
||||||
|
await InvalidSecretErrorAsync(verificationType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verificationType == VerificationType.OTP)
|
||||||
|
{
|
||||||
|
var request = new VerifyOTPRequest(secret);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _apiService.PostAccountVerifyOTPAsync(request);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await InvalidSecretErrorAsync(verificationType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(secret, null);
|
||||||
|
if (!passwordValid)
|
||||||
|
{
|
||||||
|
await InvalidSecretErrorAsync(verificationType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async private Task InvalidSecretErrorAsync(VerificationType verificationType)
|
||||||
|
{
|
||||||
|
var errorMessage = verificationType == VerificationType.OTP
|
||||||
|
? _i18nService.T("InvalidVerificationCode")
|
||||||
|
: _i18nService.T("InvalidMasterPassword");
|
||||||
|
|
||||||
|
await _platformUtilsService.ShowDialogAsync(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ namespace Bit.Core.Services
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly ITokenService _tokenService;
|
private readonly ITokenService _tokenService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly Action<bool> _lockedCallback;
|
private readonly Action<bool> _lockedCallback;
|
||||||
private readonly Func<bool, Task> _loggedOutCallback;
|
private readonly Func<bool, Task> _loggedOutCallback;
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ namespace Bit.Core.Services
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
ITokenService tokenService,
|
ITokenService tokenService,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
|
IKeyConnectorService keyConnectorService,
|
||||||
Action<bool> lockedCallback,
|
Action<bool> lockedCallback,
|
||||||
Func<bool, Task> loggedOutCallback)
|
Func<bool, Task> loggedOutCallback)
|
||||||
{
|
{
|
||||||
|
@ -49,6 +51,7 @@ namespace Bit.Core.Services
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_tokenService = tokenService;
|
_tokenService = tokenService;
|
||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
|
_keyConnectorService = keyConnectorService;
|
||||||
_lockedCallback = lockedCallback;
|
_lockedCallback = lockedCallback;
|
||||||
_loggedOutCallback = loggedOutCallback;
|
_loggedOutCallback = loggedOutCallback;
|
||||||
}
|
}
|
||||||
|
@ -119,6 +122,18 @@ namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (await _keyConnectorService.GetUsesKeyConnector()) {
|
||||||
|
var pinSet = await IsPinLockSetAsync();
|
||||||
|
var pinLock = (pinSet.Item1 && PinProtectedKey != null) || pinSet.Item2;
|
||||||
|
|
||||||
|
if (!pinLock && !await IsBiometricLockSetAsync())
|
||||||
|
{
|
||||||
|
await LogOutAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (allowSoftLock)
|
if (allowSoftLock)
|
||||||
{
|
{
|
||||||
BiometricLocked = await IsBiometricLockSetAsync();
|
BiometricLocked = await IsBiometricLockSetAsync();
|
||||||
|
|
|
@ -49,16 +49,17 @@ namespace Bit.Core.Utilities
|
||||||
i18nService, cryptoFunctionService);
|
i18nService, cryptoFunctionService);
|
||||||
searchService = new SearchService(cipherService, sendService);
|
searchService = new SearchService(cipherService, sendService);
|
||||||
var policyService = new PolicyService(storageService, userService);
|
var policyService = new PolicyService(storageService, userService);
|
||||||
|
var keyConnectorService = new KeyConnectorService(userService, cryptoService, storageService, tokenService, apiService);
|
||||||
var vaultTimeoutService = new VaultTimeoutService(cryptoService, userService, platformUtilsService,
|
var vaultTimeoutService = new VaultTimeoutService(cryptoService, userService, platformUtilsService,
|
||||||
storageService, folderService, cipherService, collectionService, searchService, messagingService, tokenService,
|
storageService, folderService, cipherService, collectionService, searchService, messagingService, tokenService,
|
||||||
policyService, null, (expired) =>
|
policyService, keyConnectorService, null, (expired) =>
|
||||||
{
|
{
|
||||||
messagingService.Send("logout", expired);
|
messagingService.Send("logout", expired);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
});
|
});
|
||||||
var syncService = new SyncService(userService, apiService, settingsService, folderService,
|
var syncService = new SyncService(userService, apiService, settingsService, folderService,
|
||||||
cipherService, cryptoService, collectionService, storageService, messagingService, policyService, sendService,
|
cipherService, cryptoService, collectionService, storageService, messagingService, policyService, sendService,
|
||||||
(bool expired) =>
|
keyConnectorService, (bool expired) =>
|
||||||
{
|
{
|
||||||
messagingService.Send("logout", expired);
|
messagingService.Send("logout", expired);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
|
@ -66,12 +67,13 @@ namespace Bit.Core.Utilities
|
||||||
var passwordGenerationService = new PasswordGenerationService(cryptoService, storageService,
|
var passwordGenerationService = new PasswordGenerationService(cryptoService, storageService,
|
||||||
cryptoFunctionService, policyService);
|
cryptoFunctionService, policyService);
|
||||||
var totpService = new TotpService(storageService, cryptoFunctionService);
|
var totpService = new TotpService(storageService, cryptoFunctionService);
|
||||||
var authService = new AuthService(cryptoService, apiService, userService, tokenService, appIdService,
|
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, userService, tokenService, appIdService,
|
||||||
i18nService, platformUtilsService, messagingService, vaultTimeoutService);
|
i18nService, platformUtilsService, messagingService, vaultTimeoutService, keyConnectorService);
|
||||||
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
||||||
var auditService = new AuditService(cryptoFunctionService, apiService);
|
var auditService = new AuditService(cryptoFunctionService, apiService);
|
||||||
var environmentService = new EnvironmentService(apiService, storageService);
|
var environmentService = new EnvironmentService(apiService, storageService);
|
||||||
var eventService = new EventService(storageService, apiService, userService, cipherService);
|
var eventService = new EventService(storageService, apiService, userService, cipherService);
|
||||||
|
var userVerificationService = new UserVerificationService(apiService, platformUtilsService, i18nService, cryptoService);
|
||||||
|
|
||||||
Register<IStateService>("stateService", stateService);
|
Register<IStateService>("stateService", stateService);
|
||||||
Register<ITokenService>("tokenService", tokenService);
|
Register<ITokenService>("tokenService", tokenService);
|
||||||
|
@ -94,6 +96,8 @@ namespace Bit.Core.Utilities
|
||||||
Register<IAuditService>("auditService", auditService);
|
Register<IAuditService>("auditService", auditService);
|
||||||
Register<IEnvironmentService>("environmentService", environmentService);
|
Register<IEnvironmentService>("environmentService", environmentService);
|
||||||
Register<IEventService>("eventService", eventService);
|
Register<IEventService>("eventService", eventService);
|
||||||
|
Register<IKeyConnectorService>("keyConnectorService", keyConnectorService);
|
||||||
|
Register<IUserVerificationService>("userVerificationService", userVerificationService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register<T>(string serviceName, T obj)
|
public static void Register<T>(string serviceName, T obj)
|
||||||
|
|
Loading…
Reference in a new issue