mirror of
https://github.com/bitwarden/mobile
synced 2026-01-09 20:13:18 +00:00
Password generator policy enforcement (#741)
* Password generator policy enforcement * Formatting * Changed to simple cast (double unboxing for int64/long) * Added ui indication of active policy on password generator page and fixed issue with switch enable logic
This commit is contained in:
@@ -12,9 +12,9 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options);
|
||||
Task<string> GeneratePasswordAsync(PasswordGenerationOptions options);
|
||||
Task<List<GeneratedPasswordHistory>> GetHistoryAsync();
|
||||
Task<PasswordGenerationOptions> GetOptionsAsync();
|
||||
Task<(PasswordGenerationOptions, PasswordGeneratorPolicyOptions)> GetOptionsAsync();
|
||||
Task<object> PasswordStrength(string password, List<string> userInputs = null);
|
||||
Task SaveOptionsAsync(PasswordGenerationOptions options);
|
||||
void NormalizeOptions(PasswordGenerationOptions options);
|
||||
void NormalizeOptions(PasswordGenerationOptions options, PasswordGeneratorPolicyOptions enforcedPolicyOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
src/Core/Models/Domain/PasswordGeneratorPolicyOptions.cs
Normal file
13
src/Core/Models/Domain/PasswordGeneratorPolicyOptions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class PasswordGeneratorPolicyOptions
|
||||
{
|
||||
public int MinLength { get; set; }
|
||||
public bool UseUppercase { get; set; }
|
||||
public bool UseLowercase { get; set; }
|
||||
public bool UseNumbers { get; set; }
|
||||
public int NumberCount { get; set; }
|
||||
public bool UseSpecial { get; set; }
|
||||
public int SpecialCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace Bit.Core.Services
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private PasswordGenerationOptions _defaultOptions = new PasswordGenerationOptions(true);
|
||||
private PasswordGenerationOptions _optionsCache;
|
||||
private List<GeneratedPasswordHistory> _history;
|
||||
@@ -30,11 +32,13 @@ namespace Bit.Core.Services
|
||||
public PasswordGenerationService(
|
||||
ICryptoService cryptoService,
|
||||
IStorageService storageService,
|
||||
ICryptoFunctionService cryptoFunctionService)
|
||||
ICryptoFunctionService cryptoFunctionService,
|
||||
IPolicyService policyService)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_storageService = storageService;
|
||||
_cryptoFunctionService = cryptoFunctionService;
|
||||
_policyService = policyService;
|
||||
}
|
||||
|
||||
public async Task<string> GeneratePasswordAsync(PasswordGenerationOptions options)
|
||||
@@ -240,7 +244,7 @@ namespace Bit.Core.Services
|
||||
return string.Join(options.WordSeparator, wordList);
|
||||
}
|
||||
|
||||
public async Task<PasswordGenerationOptions> GetOptionsAsync()
|
||||
public async Task<(PasswordGenerationOptions,PasswordGeneratorPolicyOptions)> GetOptionsAsync()
|
||||
{
|
||||
if(_optionsCache == null)
|
||||
{
|
||||
@@ -255,7 +259,129 @@ namespace Bit.Core.Services
|
||||
_optionsCache = options;
|
||||
}
|
||||
}
|
||||
return _optionsCache;
|
||||
|
||||
var enforcedPolicyOptions = await GetPasswordGeneratorPolicyOptions();
|
||||
if(enforcedPolicyOptions != null)
|
||||
{
|
||||
if(_optionsCache.Length < enforcedPolicyOptions.MinLength)
|
||||
{
|
||||
_optionsCache.Length = enforcedPolicyOptions.MinLength;
|
||||
}
|
||||
|
||||
if(enforcedPolicyOptions.UseUppercase)
|
||||
{
|
||||
_optionsCache.Uppercase = true;
|
||||
}
|
||||
|
||||
if(enforcedPolicyOptions.UseLowercase)
|
||||
{
|
||||
_optionsCache.Lowercase = true;
|
||||
}
|
||||
|
||||
if(enforcedPolicyOptions.UseNumbers)
|
||||
{
|
||||
_optionsCache.Number = true;
|
||||
}
|
||||
|
||||
if(_optionsCache.MinNumber < enforcedPolicyOptions.NumberCount)
|
||||
{
|
||||
_optionsCache.MinNumber = enforcedPolicyOptions.NumberCount;
|
||||
}
|
||||
|
||||
if(enforcedPolicyOptions.UseSpecial)
|
||||
{
|
||||
_optionsCache.Special = true;
|
||||
}
|
||||
|
||||
if(_optionsCache.MinSpecial < enforcedPolicyOptions.SpecialCount)
|
||||
{
|
||||
_optionsCache.MinSpecial = enforcedPolicyOptions.SpecialCount;
|
||||
}
|
||||
|
||||
// Must normalize these fields because the receiving call expects all options to pass the current rules
|
||||
if(_optionsCache.MinSpecial + _optionsCache.MinNumber > _optionsCache.Length)
|
||||
{
|
||||
_optionsCache.MinSpecial = _optionsCache.Length - _optionsCache.MinNumber;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// UI layer expects an instantiated object to prevent more explicit null checks
|
||||
enforcedPolicyOptions = new PasswordGeneratorPolicyOptions();
|
||||
}
|
||||
|
||||
return (_optionsCache, enforcedPolicyOptions);
|
||||
}
|
||||
|
||||
public async Task<PasswordGeneratorPolicyOptions> GetPasswordGeneratorPolicyOptions()
|
||||
{
|
||||
var policies = await _policyService.GetAll(PolicyType.PasswordGenerator);
|
||||
PasswordGeneratorPolicyOptions enforcedOptions = null;
|
||||
|
||||
if(policies == null || !policies.Any())
|
||||
{
|
||||
return enforcedOptions;
|
||||
}
|
||||
|
||||
foreach(var currentPolicy in policies)
|
||||
{
|
||||
if(!currentPolicy.Enabled || currentPolicy.Data == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(enforcedOptions == null)
|
||||
{
|
||||
enforcedOptions = new PasswordGeneratorPolicyOptions();
|
||||
}
|
||||
|
||||
var currentPolicyMinLength = currentPolicy.Data["minLength"];
|
||||
if(currentPolicyMinLength != null &&
|
||||
(int)(long)currentPolicyMinLength > enforcedOptions.MinLength)
|
||||
{
|
||||
enforcedOptions.MinLength = (int)(long)currentPolicyMinLength;
|
||||
}
|
||||
|
||||
var currentPolicyUseUpper = currentPolicy.Data["useUpper"];
|
||||
if(currentPolicyUseUpper != null && (bool)currentPolicyUseUpper)
|
||||
{
|
||||
enforcedOptions.UseUppercase = true;
|
||||
}
|
||||
|
||||
var currentPolicyUseLower = currentPolicy.Data["useLower"];
|
||||
if(currentPolicyUseLower != null && (bool)currentPolicyUseLower)
|
||||
{
|
||||
enforcedOptions.UseLowercase = true;
|
||||
}
|
||||
|
||||
var currentPolicyUseNumbers = currentPolicy.Data["useNumbers"];
|
||||
if(currentPolicyUseNumbers != null && (bool)currentPolicyUseNumbers)
|
||||
{
|
||||
enforcedOptions.UseNumbers = true;
|
||||
}
|
||||
|
||||
var currentPolicyMinNumbers = currentPolicy.Data["minNumbers"];
|
||||
if(currentPolicyMinNumbers != null &&
|
||||
(int)(long)currentPolicyMinNumbers > enforcedOptions.NumberCount)
|
||||
{
|
||||
enforcedOptions.NumberCount = (int)(long)currentPolicyMinNumbers;
|
||||
}
|
||||
|
||||
var currentPolicyUseSpecial = currentPolicy.Data["useSpecial"];
|
||||
if(currentPolicyUseSpecial != null && (bool)currentPolicyUseSpecial)
|
||||
{
|
||||
enforcedOptions.UseSpecial = true;
|
||||
}
|
||||
|
||||
var currentPolicyMinSpecial = currentPolicy.Data["minSpecial"];
|
||||
if(currentPolicyMinSpecial != null &&
|
||||
(int)(long)currentPolicyMinSpecial > enforcedOptions.SpecialCount)
|
||||
{
|
||||
enforcedOptions.SpecialCount = (int)(long)currentPolicyMinSpecial;
|
||||
}
|
||||
}
|
||||
|
||||
return enforcedOptions;
|
||||
}
|
||||
|
||||
public async Task SaveOptionsAsync(PasswordGenerationOptions options)
|
||||
@@ -315,7 +441,8 @@ namespace Bit.Core.Services
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void NormalizeOptions(PasswordGenerationOptions options)
|
||||
public void NormalizeOptions(PasswordGenerationOptions options,
|
||||
PasswordGeneratorPolicyOptions enforcedPolicyOptions)
|
||||
{
|
||||
options.MinLowercase = 0;
|
||||
options.MinUppercase = 0;
|
||||
@@ -336,6 +463,11 @@ namespace Bit.Core.Services
|
||||
options.Length = 128;
|
||||
}
|
||||
|
||||
if(options.Length < enforcedPolicyOptions.MinLength)
|
||||
{
|
||||
options.Length = enforcedPolicyOptions.MinLength;
|
||||
}
|
||||
|
||||
if(options.MinNumber == null)
|
||||
{
|
||||
options.MinNumber = 0;
|
||||
@@ -348,6 +480,11 @@ namespace Bit.Core.Services
|
||||
{
|
||||
options.MinNumber = 9;
|
||||
}
|
||||
|
||||
if(options.MinNumber < enforcedPolicyOptions.NumberCount)
|
||||
{
|
||||
options.MinNumber = enforcedPolicyOptions.NumberCount;
|
||||
}
|
||||
|
||||
if(options.MinSpecial == null)
|
||||
{
|
||||
@@ -362,6 +499,11 @@ namespace Bit.Core.Services
|
||||
options.MinSpecial = 9;
|
||||
}
|
||||
|
||||
if(options.MinSpecial < enforcedPolicyOptions.SpecialCount)
|
||||
{
|
||||
options.MinSpecial = enforcedPolicyOptions.SpecialCount;
|
||||
}
|
||||
|
||||
if(options.MinSpecial + options.MinNumber > options.Length)
|
||||
{
|
||||
options.MinSpecial = options.Length - options.MinNumber;
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Bit.Core.Utilities
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
var passwordGenerationService = new PasswordGenerationService(cryptoService, storageService,
|
||||
cryptoFunctionService);
|
||||
cryptoFunctionService, policyService);
|
||||
var totpService = new TotpService(storageService, cryptoFunctionService);
|
||||
var authService = new AuthService(cryptoService, apiService, userService, tokenService, appIdService,
|
||||
i18nService, platformUtilsService, messagingService, lockService);
|
||||
|
||||
Reference in New Issue
Block a user