1
0
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:
Matt Portune
2020-02-27 19:53:02 -05:00
committed by GitHub
parent 02cffa01e2
commit f78f303a79
9 changed files with 231 additions and 12 deletions

View File

@@ -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);
}
}
}

View 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; }
}
}

View File

@@ -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;

View File

@@ -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);