mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 10:28:28 +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()));
|
||||
});
|
||||
}
|
||||
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")
|
||||
{
|
||||
await _configService.GetAsync(true);
|
||||
|
|
|
@ -230,19 +230,18 @@ namespace Bit.App.Pages
|
|||
StartDeviceApprovalOptionsAction?.Invoke();
|
||||
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
|
||||
if (response.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
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
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label Text="{u:I18n SetMasterPasswordSummary}"
|
||||
<Label Text="{Binding SetMasterPasswordSummary}"
|
||||
StyleClass="text-md"
|
||||
HorizontalTextAlignment="Start"></Label>
|
||||
</StackLayout>
|
||||
|
|
|
@ -5,7 +5,6 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
|
@ -28,6 +27,7 @@ namespace Bit.App.Pages
|
|||
private readonly IPolicyService _policyService;
|
||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly ISyncService _syncService;
|
||||
|
||||
private bool _showPassword;
|
||||
private bool _isPolicyInEffect;
|
||||
|
@ -46,6 +46,7 @@ namespace Bit.App.Pages
|
|||
_passwordGenerationService =
|
||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>();
|
||||
|
||||
PageTitle = AppResources.SetMasterPassword;
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
|
@ -100,11 +101,17 @@ namespace Bit.App.Pages
|
|||
public Action CloseAction { get; set; }
|
||||
public string OrgIdentifier { 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()
|
||||
{
|
||||
await CheckPasswordPolicy();
|
||||
|
||||
ForceSetPasswordReason = await _stateService.GetForcePasswordResetReasonAsync();
|
||||
TriggerPropertyChanged(nameof(SetMasterPasswordSummary));
|
||||
try
|
||||
{
|
||||
var response = await _apiService.GetOrganizationAutoEnrollStatusAsync(OrgIdentifier);
|
||||
|
@ -171,8 +178,7 @@ namespace Bit.App.Pages
|
|||
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
|
||||
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
|
||||
|
||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||
var keysRequest = await GetKeysForSetPasswordRequestAsync(newUserKey);
|
||||
var request = new SetPasswordRequest
|
||||
{
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
|
@ -183,16 +189,12 @@ namespace Bit.App.Pages
|
|||
KdfMemory = kdfConfig.Memory,
|
||||
KdfParallelism = kdfConfig.Parallelism,
|
||||
OrgIdentifier = OrgIdentifier,
|
||||
Keys = new KeysRequest
|
||||
{
|
||||
PublicKey = newPublicKey,
|
||||
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||
}
|
||||
Keys = keysRequest
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||
// Set Password and relevant information
|
||||
await _apiService.SetPasswordAsync(request);
|
||||
await _stateService.SetKdfConfigurationAsync(kdfConfig);
|
||||
|
@ -200,7 +202,13 @@ namespace Bit.App.Pages
|
|||
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||
await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash);
|
||||
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)
|
||||
{
|
||||
|
@ -221,6 +229,9 @@ namespace Bit.App.Pages
|
|||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||
}
|
||||
|
||||
await _stateService.SetForcePasswordResetReasonAsync(null);
|
||||
await _stateService.SetUserHasMasterPasswordAsync(true);
|
||||
await _syncService.FullSyncAsync(true);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
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()
|
||||
{
|
||||
ShowPassword = !ShowPassword;
|
||||
|
|
|
@ -10,6 +10,7 @@ using Bit.App.Utilities;
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
@ -335,20 +336,18 @@ namespace Bit.App.Pages
|
|||
StartDeviceApprovalOptionsAction?.Invoke();
|
||||
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
|
||||
if (result.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
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
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
await TwoFactorAuthSuccessAsync();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Effects;
|
||||
using Bit.App.Models;
|
||||
|
@ -7,6 +8,7 @@ using Bit.App.Utilities;
|
|||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Forms;
|
||||
|
||||
|
@ -100,11 +102,38 @@ namespace Bit.App.Pages
|
|||
_messagingService.Send("convertAccountToKeyConnector");
|
||||
}
|
||||
|
||||
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
|
||||
await ForcePasswordResetIfNeededAsync();
|
||||
}
|
||||
|
||||
if (forcePasswordResetReason.HasValue)
|
||||
private async Task ForcePasswordResetIfNeededAsync()
|
||||
{
|
||||
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
|
||||
switch (forcePasswordResetReason)
|
||||
{
|
||||
_messagingService.Send(Constants.ForceUpdatePassword);
|
||||
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);
|
||||
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>
|
||||
/// Looks up a localized string similar to Your request has been sent to your admin..
|
||||
/// </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">
|
||||
<value>Account logged out.</value>
|
||||
</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">
|
||||
<value>Set up an unlock option to change your vault timeout action.</value>
|
||||
</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);
|
||||
Task<bool> GetShouldTrustDeviceAsync();
|
||||
Task SetShouldTrustDeviceAsync(bool value);
|
||||
Task SetUserHasMasterPasswordAsync(bool value, string userId = null);
|
||||
Task<Region?> GetActiveUserRegionAsync();
|
||||
Task<Region?> GetPreAuthRegionAsync();
|
||||
Task SetPreAuthRegionAsync(Region value);
|
||||
|
||||
[Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||
Task<string> GetPinProtectedAsync(string userId = null);
|
||||
[Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
|
||||
|
|
|
@ -57,6 +57,7 @@ namespace Bit.Core
|
|||
public const string AppLocaleKey = "appLocale";
|
||||
public const string ClearSensitiveFields = "clearSensitiveFields";
|
||||
public const string ForceUpdatePassword = "forceUpdatePassword";
|
||||
public const string ForceSetPassword = "forceSetPassword";
|
||||
public const string ShouldTrustDevice = "shouldTrustDevice";
|
||||
public const int SelectFileRequestCode = 42;
|
||||
public const int SelectFilePermissionRequestCode = 43;
|
||||
|
|
|
@ -15,5 +15,6 @@
|
|||
public bool ManagePolicies { get; set; }
|
||||
public bool ManageSso { 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
|
||||
/// policy that is enforced on login.
|
||||
/// </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 Key { get; set; }
|
||||
public string MasterPasswordHint { get; set; }
|
||||
public KeysRequest Keys { get; set; }
|
||||
public KeysRequest? Keys { get; set; }
|
||||
public KdfType Kdf { get; set; }
|
||||
public int KdfIterations { get; set; }
|
||||
public int? KdfMemory { get; set; }
|
||||
|
|
|
@ -20,5 +20,6 @@ namespace Bit.Core.Models.Response
|
|||
public List<ProfileOrganizationResponse> Organizations { get; set; }
|
||||
public bool UsesKeyConnector { 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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return await GetActiveUserCustomDataAsync(a => a?.Settings?.Region);
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
|
@ -398,6 +399,33 @@ namespace Bit.Core.Services
|
|||
await _stateService.SetPersonalPremiumAsync(response.Premium);
|
||||
await _stateService.SetAvatarColorAsync(response.AvatarColor);
|
||||
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)
|
||||
|
|
Loading…
Reference in a new issue