mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 02:48:29 +03:00
[PM-3273][PM-4679] New owner/admin permission on login (#2837)
* [PM-3273] Add property for password set. Add labels. Update sync service. * [PM-3273] Set password needs set in state. Read value on sync and nav to page. * [PM-3273] Add navigation to Set Password on vault landing if needed. * [PM-3273] Update SetPasswordPage copy * [PM-3273] Add ManageResetPassword to Org Permissions, handle it on sync. * [PM-3273] Change user has master password state when set master password is complete. * [PM-3273] Code clean up * [PM-3273] Remove unnecessary property from account profile * [PM-3273] Add check for remembered org identifier * [PM-4679] Added logging calls for future checks. --------- Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
This commit is contained in:
parent
3a13ba4efa
commit
793c5fef6f
42 changed files with 203544 additions and 33 deletions
|
@ -171,6 +171,11 @@ namespace Bit.App
|
||||||
new NavigationPage(new UpdateTempPasswordPage()));
|
new NavigationPage(new UpdateTempPasswordPage()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (message.Command == Constants.ForceSetPassword)
|
||||||
|
{
|
||||||
|
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(
|
||||||
|
new NavigationPage(new SetPasswordPage(orgIdentifier: (string)message.Data))));
|
||||||
|
}
|
||||||
else if (message.Command == "syncCompleted")
|
else if (message.Command == "syncCompleted")
|
||||||
{
|
{
|
||||||
await _configService.GetAsync(true);
|
await _configService.GetAsync(true);
|
||||||
|
|
|
@ -230,19 +230,18 @@ namespace Bit.App.Pages
|
||||||
StartDeviceApprovalOptionsAction?.Invoke();
|
StartDeviceApprovalOptionsAction?.Invoke();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
|
||||||
if (!decryptOptions.HasMasterPassword &&
|
|
||||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
|
||||||
{
|
|
||||||
StartSetPasswordAction?.Invoke();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
||||||
if (response.ForcePasswordReset)
|
if (response.ForcePasswordReset)
|
||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||||
|
if (!decryptOptions.HasMasterPassword &&
|
||||||
|
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||||
|
{
|
||||||
|
await _stateService.SetForcePasswordResetReasonAsync(ForcePasswordResetReason.TdeUserWithoutPasswordHasPasswordResetPermission);
|
||||||
|
}
|
||||||
// Device is trusted and has keys, so we can decrypt
|
// Device is trusted and has keys, so we can decrypt
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
SsoAuthSuccessAction?.Invoke();
|
SsoAuthSuccessAction?.Invoke();
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<StackLayout Spacing="20">
|
<StackLayout Spacing="20">
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label Text="{u:I18n SetMasterPasswordSummary}"
|
<Label Text="{Binding SetMasterPasswordSummary}"
|
||||||
StyleClass="text-md"
|
StyleClass="text-md"
|
||||||
HorizontalTextAlignment="Start"></Label>
|
HorizontalTextAlignment="Start"></Label>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
|
@ -5,7 +5,6 @@ using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
@ -28,6 +27,7 @@ namespace Bit.App.Pages
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
|
private readonly ISyncService _syncService;
|
||||||
|
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private bool _isPolicyInEffect;
|
private bool _isPolicyInEffect;
|
||||||
|
@ -46,6 +46,7 @@ namespace Bit.App.Pages
|
||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
_syncService = ServiceContainer.Resolve<ISyncService>();
|
||||||
|
|
||||||
PageTitle = AppResources.SetMasterPassword;
|
PageTitle = AppResources.SetMasterPassword;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
@ -100,11 +101,17 @@ namespace Bit.App.Pages
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
public string OrgIdentifier { get; set; }
|
public string OrgIdentifier { get; set; }
|
||||||
public string OrgId { get; set; }
|
public string OrgId { get; set; }
|
||||||
|
public ForcePasswordResetReason? ForceSetPasswordReason { get; private set; }
|
||||||
|
|
||||||
|
public string SetMasterPasswordSummary => ForceSetPasswordReason == ForcePasswordResetReason.TdeUserWithoutPasswordHasPasswordResetPermission
|
||||||
|
? AppResources.YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword
|
||||||
|
: AppResources.YourOrganizationRequiresYouToSetAMasterPassword;
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
await CheckPasswordPolicy();
|
await CheckPasswordPolicy();
|
||||||
|
ForceSetPasswordReason = await _stateService.GetForcePasswordResetReasonAsync();
|
||||||
|
TriggerPropertyChanged(nameof(SetMasterPasswordSummary));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await _apiService.GetOrganizationAutoEnrollStatusAsync(OrgIdentifier);
|
var response = await _apiService.GetOrganizationAutoEnrollStatusAsync(OrgIdentifier);
|
||||||
|
@ -171,8 +178,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
|
||||||
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
|
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
|
||||||
|
var keysRequest = await GetKeysForSetPasswordRequestAsync(newUserKey);
|
||||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
|
||||||
var request = new SetPasswordRequest
|
var request = new SetPasswordRequest
|
||||||
{
|
{
|
||||||
MasterPasswordHash = masterPasswordHash,
|
MasterPasswordHash = masterPasswordHash,
|
||||||
|
@ -183,16 +189,12 @@ namespace Bit.App.Pages
|
||||||
KdfMemory = kdfConfig.Memory,
|
KdfMemory = kdfConfig.Memory,
|
||||||
KdfParallelism = kdfConfig.Parallelism,
|
KdfParallelism = kdfConfig.Parallelism,
|
||||||
OrgIdentifier = OrgIdentifier,
|
OrgIdentifier = OrgIdentifier,
|
||||||
Keys = new KeysRequest
|
Keys = keysRequest
|
||||||
{
|
|
||||||
PublicKey = newPublicKey,
|
|
||||||
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
// Set Password and relevant information
|
// Set Password and relevant information
|
||||||
await _apiService.SetPasswordAsync(request);
|
await _apiService.SetPasswordAsync(request);
|
||||||
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
||||||
|
@ -200,7 +202,13 @@ namespace Bit.App.Pages
|
||||||
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||||
await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash);
|
await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString);
|
||||||
await _cryptoService.SetUserPrivateKeyAsync(newProtectedPrivateKey.EncryptedString);
|
|
||||||
|
// Set private key only for new JIT provisioned users in MP encryption orgs
|
||||||
|
// Existing TDE users will have private key set on sync or on login
|
||||||
|
if (keysRequest != null)
|
||||||
|
{
|
||||||
|
await _cryptoService.SetUserPrivateKeyAsync(keysRequest.EncryptedPrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
if (ResetPasswordAutoEnroll)
|
if (ResetPasswordAutoEnroll)
|
||||||
{
|
{
|
||||||
|
@ -221,6 +229,9 @@ namespace Bit.App.Pages
|
||||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _stateService.SetForcePasswordResetReasonAsync(null);
|
||||||
|
await _stateService.SetUserHasMasterPasswordAsync(true);
|
||||||
|
await _syncService.FullSyncAsync(true);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
SetPasswordSuccessAction?.Invoke();
|
SetPasswordSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
@ -235,6 +246,21 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<KeysRequest> GetKeysForSetPasswordRequestAsync(UserKey newUserKey)
|
||||||
|
{
|
||||||
|
if (ForceSetPasswordReason == ForcePasswordResetReason.TdeUserWithoutPasswordHasPasswordResetPermission)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||||
|
return new KeysRequest
|
||||||
|
{
|
||||||
|
PublicKey = newPublicKey,
|
||||||
|
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
@ -335,20 +336,18 @@ namespace Bit.App.Pages
|
||||||
StartDeviceApprovalOptionsAction?.Invoke();
|
StartDeviceApprovalOptionsAction?.Invoke();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
|
||||||
if (!decryptOptions.HasMasterPassword &&
|
|
||||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
|
||||||
{
|
|
||||||
StartSetPasswordAction?.Invoke();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
||||||
if (result.ForcePasswordReset)
|
if (result.ForcePasswordReset)
|
||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||||
|
if (!decryptOptions.HasMasterPassword &&
|
||||||
|
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||||
|
{
|
||||||
|
await _stateService.SetForcePasswordResetReasonAsync(ForcePasswordResetReason.TdeUserWithoutPasswordHasPasswordResetPermission);
|
||||||
|
}
|
||||||
// Device is trusted and has keys, so we can decrypt
|
// Device is trusted and has keys, so we can decrypt
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
await TwoFactorAuthSuccessAsync();
|
await TwoFactorAuthSuccessAsync();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Effects;
|
using Bit.App.Effects;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
|
@ -7,6 +8,7 @@ using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
@ -100,11 +102,38 @@ namespace Bit.App.Pages
|
||||||
_messagingService.Send("convertAccountToKeyConnector");
|
_messagingService.Send("convertAccountToKeyConnector");
|
||||||
}
|
}
|
||||||
|
|
||||||
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
|
await ForcePasswordResetIfNeededAsync();
|
||||||
|
}
|
||||||
|
|
||||||
if (forcePasswordResetReason.HasValue)
|
private async Task ForcePasswordResetIfNeededAsync()
|
||||||
{
|
{
|
||||||
|
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
|
||||||
|
switch (forcePasswordResetReason)
|
||||||
|
{
|
||||||
|
case ForcePasswordResetReason.TdeUserWithoutPasswordHasPasswordResetPermission:
|
||||||
|
// TDE users should only have one org
|
||||||
|
var userOrgs = await _stateService.GetOrganizationsAsync();
|
||||||
|
if (userOrgs != null && userOrgs.Any())
|
||||||
|
{
|
||||||
|
_messagingService.Send(Constants.ForceSetPassword, userOrgs.First().Value.Identifier);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_logger.Value.Error("TDE user needs to set password but has no organizations.");
|
||||||
|
|
||||||
|
var rememberedOrg = _stateService.GetRememberedOrgIdentifierAsync();
|
||||||
|
if (rememberedOrg == null)
|
||||||
|
{
|
||||||
|
_logger.Value.Error("TDE user needs to set password but has no organizations or remembered org identifier.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_messagingService.Send(Constants.ForceSetPassword, rememberedOrg);
|
||||||
|
return;
|
||||||
|
case ForcePasswordResetReason.AdminForcePasswordReset:
|
||||||
|
case ForcePasswordResetReason.WeakMasterPasswordOnLogin:
|
||||||
_messagingService.Send(Constants.ForceUpdatePassword);
|
_messagingService.Send(Constants.ForceUpdatePassword);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
|
@ -7775,6 +7775,24 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Your organization permissions were updated, requiring you to set a master password..
|
||||||
|
/// </summary>
|
||||||
|
public static string YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Your organization requires you to set a master password..
|
||||||
|
/// </summary>
|
||||||
|
public static string YourOrganizationRequiresYouToSetAMasterPassword {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("YourOrganizationRequiresYouToSetAMasterPassword", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Your request has been sent to your admin..
|
/// Looks up a localized string similar to Your request has been sent to your admin..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
7821
src/App/Resources/AppResources.cs.Designer.cs
generated
7821
src/App/Resources/AppResources.cs.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.da.Designer.cs
generated
7822
src/App/Resources/AppResources.da.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7821
src/App/Resources/AppResources.de.Designer.cs
generated
7821
src/App/Resources/AppResources.de.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7823
src/App/Resources/AppResources.es.Designer.cs
generated
7823
src/App/Resources/AppResources.es.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7823
src/App/Resources/AppResources.fi.Designer.cs
generated
7823
src/App/Resources/AppResources.fi.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7823
src/App/Resources/AppResources.fr.Designer.cs
generated
7823
src/App/Resources/AppResources.fr.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7823
src/App/Resources/AppResources.hi.Designer.cs
generated
7823
src/App/Resources/AppResources.hi.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7820
src/App/Resources/AppResources.hr.Designer.cs
generated
7820
src/App/Resources/AppResources.hr.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7821
src/App/Resources/AppResources.hu.Designer.cs
generated
7821
src/App/Resources/AppResources.hu.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.id.Designer.cs
generated
7822
src/App/Resources/AppResources.id.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.it.Designer.cs
generated
7822
src/App/Resources/AppResources.it.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.ja.Designer.cs
generated
7822
src/App/Resources/AppResources.ja.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.nl.Designer.cs
generated
7822
src/App/Resources/AppResources.nl.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.pl.Designer.cs
generated
7822
src/App/Resources/AppResources.pl.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7823
src/App/Resources/AppResources.pt-BR.Designer.cs
generated
7823
src/App/Resources/AppResources.pt-BR.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7821
src/App/Resources/AppResources.pt-PT.Designer.cs
generated
7821
src/App/Resources/AppResources.pt-PT.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -2862,6 +2862,12 @@ Do you want to switch to this account?</value>
|
||||||
<data name="AccountLoggedOutBiometricExceeded" xml:space="preserve">
|
<data name="AccountLoggedOutBiometricExceeded" xml:space="preserve">
|
||||||
<value>Account logged out.</value>
|
<value>Account logged out.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" xml:space="preserve">
|
||||||
|
<value>Your organization permissions were updated, requiring you to set a master password.</value>
|
||||||
|
</data>
|
||||||
|
<data name="YourOrganizationRequiresYouToSetAMasterPassword" xml:space="preserve">
|
||||||
|
<value>Your organization requires you to set a master password.</value>
|
||||||
|
</data>
|
||||||
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
|
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
|
||||||
<value>Set up an unlock option to change your vault timeout action.</value>
|
<value>Set up an unlock option to change your vault timeout action.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
7822
src/App/Resources/AppResources.ro.Designer.cs
generated
7822
src/App/Resources/AppResources.ro.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7824
src/App/Resources/AppResources.ru.Designer.cs
generated
7824
src/App/Resources/AppResources.ru.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.sk.Designer.cs
generated
7822
src/App/Resources/AppResources.sk.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7824
src/App/Resources/AppResources.sv.Designer.cs
generated
7824
src/App/Resources/AppResources.sv.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7830
src/App/Resources/AppResources.th.Designer.cs
generated
7830
src/App/Resources/AppResources.th.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7821
src/App/Resources/AppResources.tr.Designer.cs
generated
7821
src/App/Resources/AppResources.tr.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.uk.Designer.cs
generated
7822
src/App/Resources/AppResources.uk.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7823
src/App/Resources/AppResources.vi.Designer.cs
generated
7823
src/App/Resources/AppResources.vi.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.zh-Hans.Designer.cs
generated
7822
src/App/Resources/AppResources.zh-Hans.Designer.cs
generated
File diff suppressed because it is too large
Load diff
7822
src/App/Resources/AppResources.zh-Hant.Designer.cs
generated
7822
src/App/Resources/AppResources.zh-Hant.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -185,10 +185,10 @@ namespace Bit.Core.Abstractions
|
||||||
void SetConfigs(ConfigResponse value);
|
void SetConfigs(ConfigResponse value);
|
||||||
Task<bool> GetShouldTrustDeviceAsync();
|
Task<bool> GetShouldTrustDeviceAsync();
|
||||||
Task SetShouldTrustDeviceAsync(bool value);
|
Task SetShouldTrustDeviceAsync(bool value);
|
||||||
|
Task SetUserHasMasterPasswordAsync(bool value, string userId = null);
|
||||||
Task<Region?> GetActiveUserRegionAsync();
|
Task<Region?> GetActiveUserRegionAsync();
|
||||||
Task<Region?> GetPreAuthRegionAsync();
|
Task<Region?> GetPreAuthRegionAsync();
|
||||||
Task SetPreAuthRegionAsync(Region value);
|
Task SetPreAuthRegionAsync(Region value);
|
||||||
|
|
||||||
[Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
[Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
Task<string> GetPinProtectedAsync(string userId = null);
|
Task<string> GetPinProtectedAsync(string userId = null);
|
||||||
[Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
[Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||||
|
|
|
@ -57,6 +57,7 @@ namespace Bit.Core
|
||||||
public const string AppLocaleKey = "appLocale";
|
public const string AppLocaleKey = "appLocale";
|
||||||
public const string ClearSensitiveFields = "clearSensitiveFields";
|
public const string ClearSensitiveFields = "clearSensitiveFields";
|
||||||
public const string ForceUpdatePassword = "forceUpdatePassword";
|
public const string ForceUpdatePassword = "forceUpdatePassword";
|
||||||
|
public const string ForceSetPassword = "forceSetPassword";
|
||||||
public const string ShouldTrustDevice = "shouldTrustDevice";
|
public const string ShouldTrustDevice = "shouldTrustDevice";
|
||||||
public const int SelectFileRequestCode = 42;
|
public const int SelectFileRequestCode = 42;
|
||||||
public const int SelectFilePermissionRequestCode = 43;
|
public const int SelectFilePermissionRequestCode = 43;
|
||||||
|
|
|
@ -15,5 +15,6 @@
|
||||||
public bool ManagePolicies { get; set; }
|
public bool ManagePolicies { get; set; }
|
||||||
public bool ManageSso { get; set; }
|
public bool ManageSso { get; set; }
|
||||||
public bool ManageUsers { get; set; }
|
public bool ManageUsers { get; set; }
|
||||||
|
public bool ManageResetPassword { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,12 @@
|
||||||
/// Occurs when a user logs in with a master password that does not meet an organization's master password
|
/// Occurs when a user logs in with a master password that does not meet an organization's master password
|
||||||
/// policy that is enforced on login.
|
/// policy that is enforced on login.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
WeakMasterPasswordOnLogin
|
WeakMasterPasswordOnLogin,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a TDE user without a password obtains the password reset permission.
|
||||||
|
/// Set post login & decryption client side and by server in sync (to catch logged in users).
|
||||||
|
/// </summary>
|
||||||
|
TdeUserWithoutPasswordHasPasswordResetPermission,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace Bit.Core.Models.Request
|
||||||
public string MasterPasswordHash { get; set; }
|
public string MasterPasswordHash { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public string MasterPasswordHint { get; set; }
|
public string MasterPasswordHint { get; set; }
|
||||||
public KeysRequest Keys { get; set; }
|
public KeysRequest? Keys { get; set; }
|
||||||
public KdfType Kdf { get; set; }
|
public KdfType Kdf { get; set; }
|
||||||
public int KdfIterations { get; set; }
|
public int KdfIterations { get; set; }
|
||||||
public int? KdfMemory { get; set; }
|
public int? KdfMemory { get; set; }
|
||||||
|
|
|
@ -20,5 +20,6 @@ namespace Bit.Core.Models.Response
|
||||||
public List<ProfileOrganizationResponse> Organizations { get; set; }
|
public List<ProfileOrganizationResponse> Organizations { get; set; }
|
||||||
public bool UsesKeyConnector { get; set; }
|
public bool UsesKeyConnector { get; set; }
|
||||||
public string AvatarColor { get; set; }
|
public string AvatarColor { get; set; }
|
||||||
|
public bool HasManageResetPasswordPermission { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1363,6 +1363,15 @@ namespace Bit.Core.Services
|
||||||
_storageMediatorService.Save(Constants.ConfigsKey, value);
|
_storageMediatorService.Save(Constants.ConfigsKey, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SetUserHasMasterPasswordAsync(bool value, string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
var account = await GetAccountAsync(reconciledOptions);
|
||||||
|
account.Profile.UserDecryptionOptions.HasMasterPassword = value;
|
||||||
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Region?> GetActiveUserRegionAsync()
|
public async Task<Region?> GetActiveUserRegionAsync()
|
||||||
{
|
{
|
||||||
return await GetActiveUserCustomDataAsync(a => a?.Settings?.Region);
|
return await GetActiveUserCustomDataAsync(a => a?.Settings?.Region);
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Response;
|
using Bit.Core.Models.Response;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
@ -398,6 +399,33 @@ namespace Bit.Core.Services
|
||||||
await _stateService.SetPersonalPremiumAsync(response.Premium);
|
await _stateService.SetPersonalPremiumAsync(response.Premium);
|
||||||
await _stateService.SetAvatarColorAsync(response.AvatarColor);
|
await _stateService.SetAvatarColorAsync(response.AvatarColor);
|
||||||
await _keyConnectorService.SetUsesKeyConnectorAsync(response.UsesKeyConnector);
|
await _keyConnectorService.SetUsesKeyConnectorAsync(response.UsesKeyConnector);
|
||||||
|
await SetPasswordSetReasonIfNeededAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetPasswordSetReasonIfNeededAsync(ProfileResponse response)
|
||||||
|
{
|
||||||
|
// The `ForcePasswordReset` flag indicates an admin has reset the user's password and must be updated
|
||||||
|
if (response.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
await _stateService.SetForcePasswordResetReasonAsync(ForcePasswordResetReason.AdminForcePasswordReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasManageResetPasswordPermission = response.Organizations.Any(org =>
|
||||||
|
org.Type == Enums.OrganizationUserType.Owner ||
|
||||||
|
org.Type == Enums.OrganizationUserType.Admin ||
|
||||||
|
org.Permissions?.ManageResetPassword == true);
|
||||||
|
if (!hasManageResetPasswordPermission)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var decryptionOptions = await _stateService.GetAccountDecryptionOptions();
|
||||||
|
if (decryptionOptions?.HasMasterPassword == false)
|
||||||
|
{
|
||||||
|
// TDE user w/out MP went from having no password reset permission to having it.
|
||||||
|
// Must set the force password reset reason so the auth guard will redirect to the set password page.
|
||||||
|
await _stateService.SetForcePasswordResetReasonAsync(ForcePasswordResetReason.TdeUserWithoutPasswordHasPasswordResetPermission);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SyncFoldersAsync(string userId, List<FolderResponse> response)
|
private async Task SyncFoldersAsync(string userId, List<FolderResponse> response)
|
||||||
|
|
Loading…
Reference in a new issue