1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-09-27 03:52:57 +02:00

[PM-1402] Refactor PasswordGenerationService alongside PolicyService (#2443)

* PM-1402 Refactor pass generation service alongside policyservice

* PM-1402 Refactor PasswordGenerationService and PolicyService to have a simpler code and more specific to each class

* PM-1402 Fix format

* PM-1402 Moved policy consts from PolicyService to Policy

* PM-1402 fix crash due to lack of null checking

* PM-1402 fix format

* PM-1402 removed GetValueOrDefault() given that it was not needed and was changing the behavior
This commit is contained in:
Federico Maccaroni 2023-05-11 18:41:32 +02:00 committed by GitHub
parent f24b82f345
commit 1c8328f62d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 282 additions and 352 deletions

View File

@ -121,9 +121,8 @@ namespace Bit.App.Pages
var ssoToken = response.Token;
var passwordOptions = new PasswordGenerationOptions(true);
passwordOptions.Length = 64;
var passwordOptions = PasswordGenerationOptions.CreateDefault
.WithLength(64);
var codeVerifier = await _passwordGenerationService.GeneratePasswordAsync(passwordOptions);
var codeVerifierHash = await _cryptoFunctionService.HashAsync(codeVerifier, CryptoHashAlgorithm.Sha256);

View File

@ -827,7 +827,7 @@ namespace Bit.App.Pages
private void SetOptions()
{
_options.AllowAmbiguousChar = AllowAmbiguousChars;
_options.Type = PasswordTypeSelectedIndex == 1 ? "passphrase" : "password";
_options.Type = PasswordTypeSelectedIndex == 1 ? PasswordGenerationOptions.TYPE_PASSPHRASE : PasswordGenerationOptions.TYPE_PASSWORD;
_options.MinNumber = MinNumber;
_options.MinSpecial = MinSpecial;
_options.Special = Special;

View File

@ -131,7 +131,7 @@ namespace Bit.App.Pages
{
// if we have a vault timeout policy, we need to filter the timeout options
_vaultTimeoutPolicy = (await _policyService.GetAll(PolicyType.MaximumVaultTimeout)).First();
var policyMinutes = _policyService.GetPolicyInt(_vaultTimeoutPolicy, PolicyService.TIMEOUT_POLICY_MINUTES);
var policyMinutes = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY);
_vaultTimeoutOptions = _vaultTimeoutOptions.Where(t =>
t.Value <= policyMinutes &&
(t.Value > 0 || t.Value == CustomVaultTimeoutValue) &&
@ -302,7 +302,7 @@ namespace Bit.App.Pages
if (_vaultTimeoutPolicy != null)
{
var maximumTimeout = _policyService.GetPolicyInt(_vaultTimeoutPolicy, PolicyService.TIMEOUT_POLICY_MINUTES);
var maximumTimeout = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY);
if (newTimeout > maximumTimeout)
{
@ -382,7 +382,7 @@ namespace Bit.App.Pages
public async Task VaultTimeoutActionAsync()
{
if (_vaultTimeoutPolicy != null &&
!string.IsNullOrEmpty(_policyService.GetPolicyString(_vaultTimeoutPolicy, PolicyService.TIMEOUT_POLICY_ACTION)))
!string.IsNullOrEmpty(_vaultTimeoutPolicy.GetString(Policy.MINUTES_KEY)))
{
// do nothing if we have a policy set
return;
@ -610,8 +610,8 @@ namespace Bit.App.Pages
}
if (_vaultTimeoutPolicy != null)
{
var policyMinutes = _policyService.GetPolicyInt(_vaultTimeoutPolicy, PolicyService.TIMEOUT_POLICY_MINUTES);
var policyAction = _policyService.GetPolicyString(_vaultTimeoutPolicy, PolicyService.TIMEOUT_POLICY_ACTION);
var policyMinutes = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY);
var policyAction = _vaultTimeoutPolicy.GetString(Policy.ACTION_KEY);
if (policyMinutes.HasValue || !string.IsNullOrWhiteSpace(policyAction))
{
@ -625,14 +625,14 @@ namespace Bit.App.Pages
else if (!policyMinutes.HasValue && !string.IsNullOrWhiteSpace(policyAction))
{
policyAlert = string.Format(AppResources.VaultTimeoutActionPolicyInEffect,
policyAction == PolicyService.TIMEOUT_POLICY_ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
policyAction == Policy.ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
}
else
{
policyAlert = string.Format(AppResources.VaultTimeoutPolicyWithActionInEffect,
Math.Floor((float)policyMinutes / 60),
policyMinutes % 60,
policyAction == PolicyService.TIMEOUT_POLICY_ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
policyAction == Policy.ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
}
securityItems.Insert(0, new SettingsPageListItem
{

View File

@ -15,8 +15,6 @@ namespace Bit.Core.Abstractions
Task<string> GeneratePasswordAsync(PasswordGenerationOptions options);
Task<List<GeneratedPasswordHistory>> GetHistoryAsync();
Task<(PasswordGenerationOptions, PasswordGeneratorPolicyOptions)> GetOptionsAsync();
Task<(PasswordGenerationOptions, PasswordGeneratorPolicyOptions)>
EnforcePasswordGeneratorPoliciesOnOptionsAsync(PasswordGenerationOptions options);
Result PasswordStrength(string password, List<string> userInputs = null);
Task SaveOptionsAsync(PasswordGenerationOptions options);
void NormalizeOptions(PasswordGenerationOptions options, PasswordGeneratorPolicyOptions enforcedPolicyOptions);

View File

@ -19,8 +19,7 @@ namespace Bit.Core.Abstractions
Tuple<ResetPasswordPolicyOptions, bool> GetResetPasswordPolicyOptions(IEnumerable<Policy> policies,
string orgId);
Task<bool> PolicyAppliesToUser(PolicyType policyType, Func<Policy, bool> policyFilter = null, string userId = null);
int? GetPolicyInt(Policy policy, string key);
string GetPolicyString(Policy policy, string key);
Task<bool> ShouldShowVaultFilterAsync();
Task<PasswordGeneratorPolicyOptions> GetPasswordGeneratorPolicyOptionsAsync();
}
}

View File

@ -2,29 +2,29 @@
{
public class PasswordGenerationOptions
{
public PasswordGenerationOptions() { }
public const string TYPE_PASSWORD = "password";
public const string TYPE_PASSPHRASE = "passphrase";
public PasswordGenerationOptions(bool defaultOptions)
public static PasswordGenerationOptions CreateDefault => new PasswordGenerationOptions
{
if (defaultOptions)
{
Length = 14;
AllowAmbiguousChar = true;
Number = true;
MinNumber = 1;
Uppercase = true;
MinUppercase = 0;
Lowercase = true;
MinLowercase = 0;
Special = false;
MinSpecial = 1;
Type = "password";
NumWords = 3;
WordSeparator = "-";
Capitalize = false;
IncludeNumber = false;
}
}
Length = 14,
AllowAmbiguousChar = true,
Number = true,
MinNumber = 1,
Uppercase = true,
MinUppercase = 0,
Lowercase = true,
MinLowercase = 0,
Special = false,
MinSpecial = 1,
Type = TYPE_PASSWORD,
NumWords = 3,
WordSeparator = "-",
Capitalize = false,
IncludeNumber = false
};
public PasswordGenerationOptions() { }
public int? Length { get; set; }
public bool? AllowAmbiguousChar { get; set; }
@ -42,6 +42,12 @@
public bool? Capitalize { get; set; }
public bool? IncludeNumber { get; set; }
public PasswordGenerationOptions WithLength(int? length)
{
Length = length;
return this;
}
public void Merge(PasswordGenerationOptions defaults)
{
Length = Length ?? defaults.Length;
@ -60,5 +66,75 @@
Capitalize = Capitalize ?? defaults.Capitalize;
IncludeNumber = IncludeNumber ?? defaults.IncludeNumber;
}
public void EnforcePolicy(PasswordGeneratorPolicyOptions enforcedPolicyOptions)
{
if (enforcedPolicyOptions is null)
{
return;
}
if (Length < enforcedPolicyOptions.MinLength)
{
Length = enforcedPolicyOptions.MinLength;
}
if (enforcedPolicyOptions.UseUppercase)
{
Uppercase = true;
}
if (enforcedPolicyOptions.UseLowercase)
{
Lowercase = true;
}
if (enforcedPolicyOptions.UseNumbers)
{
Number = true;
}
if (MinNumber < enforcedPolicyOptions.NumberCount)
{
MinNumber = enforcedPolicyOptions.NumberCount;
}
if (enforcedPolicyOptions.UseSpecial)
{
Special = true;
}
if (MinSpecial < enforcedPolicyOptions.SpecialCount)
{
MinSpecial = enforcedPolicyOptions.SpecialCount;
}
// Must normalize these fields because the receiving call expects all options to pass the current rules
if (MinSpecial + MinNumber > Length)
{
MinSpecial = Length - MinNumber;
}
if (NumWords < enforcedPolicyOptions.MinNumberOfWords)
{
NumWords = enforcedPolicyOptions.MinNumberOfWords;
}
if (enforcedPolicyOptions.Capitalize)
{
Capitalize = true;
}
if (enforcedPolicyOptions.IncludeNumber)
{
IncludeNumber = true;
}
// Force default type if password/passphrase selected via policy
if (enforcedPolicyOptions.DefaultType == TYPE_PASSWORD || enforcedPolicyOptions.DefaultType == TYPE_PASSPHRASE)
{
Type = enforcedPolicyOptions.DefaultType;
}
}
}
}

View File

@ -6,6 +6,11 @@ namespace Bit.Core.Models.Domain
{
public class Policy : Domain
{
public const string MINUTES_KEY = "minutes";
public const string ACTION_KEY = "action";
public const string ACTION_LOCK = "lock";
public const string ACTION_LOGOUT = "logOut";
public Policy() { }
public Policy(PolicyData obj)
@ -22,5 +27,32 @@ namespace Bit.Core.Models.Domain
public PolicyType Type { get; set; }
public Dictionary<string, object> Data { get; set; }
public bool Enabled { get; set; }
public int? GetInt(string key)
{
if (Data.TryGetValue(key, out var val) && val != null)
{
return (int)(long)val;
}
return null;
}
public bool? GetBool(string key)
{
if (Data.TryGetValue(key, out var val) && val != null)
{
return (bool)val;
}
return null;
}
public string GetString(string key)
{
if (Data.TryGetValue(key, out var val))
{
return (string)val;
}
return null;
}
}
}

View File

@ -605,7 +605,7 @@ namespace Bit.Core.Services
var generatedFingerprintPhrase = await _cryptoService.GetFingerprintAsync(email, keyPair.Item1);
var fingerprintPhrase = string.Join("-", generatedFingerprintPhrase);
var publicB64 = Convert.ToBase64String(keyPair.Item1);
var accessCode = await _passwordGenerationService.GeneratePasswordAsync(new PasswordGenerationOptions(true) { Length = 25 });
var accessCode = await _passwordGenerationService.GeneratePasswordAsync(PasswordGenerationOptions.CreateDefault.WithLength(25));
var passwordlessCreateLoginRequest = new PasswordlessCreateLoginRequest(email, publicB64, deviceId, accessCode, AuthRequestType.AuthenticateAndUnlock, fingerprintPhrase);
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest);

View File

@ -6,7 +6,6 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using Zxcvbn;
@ -15,17 +14,17 @@ namespace Bit.Core.Services
{
public class PasswordGenerationService : IPasswordGenerationService
{
private const int MaxPasswordsInHistory = 100;
private const string LowercaseCharSet = "abcdefghijkmnopqrstuvwxyz";
private const string UppercaseCharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ";
private const string NumberCharSet = "23456789";
private const string SpecialCharSet = "!@#$%^&*";
private const int MAX_PASSWORDS_IN_HISTORY = 100;
private const string LOWERCASE_CHAR_SET = "abcdefghijkmnopqrstuvwxyz";
private const string UPPERCASE_CHAR_SET = "ABCDEFGHJKLMNPQRSTUVWXYZ";
private const string NUMER_CHAR_SET = "23456789";
private const string SPECIAL_CHAR_SET = "!@#$%^&*";
private readonly ICryptoService _cryptoService;
private readonly IStateService _stateService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IPolicyService _policyService;
private PasswordGenerationOptions _defaultOptions = new PasswordGenerationOptions(true);
private PasswordGenerationOptions _defaultOptions = PasswordGenerationOptions.CreateDefault;
private PasswordGenerationOptions _optionsCache;
private List<GeneratedPasswordHistory> _history;
@ -45,7 +44,7 @@ namespace Bit.Core.Services
{
// Overload defaults with given options
options.Merge(_defaultOptions);
if (options.Type == "passphrase")
if (options.Type == PasswordGenerationOptions.TYPE_PASSPHRASE)
{
return await GeneratePassphraseAsync(options);
}
@ -54,30 +53,30 @@ namespace Bit.Core.Services
SanitizePasswordLength(options, true);
var positionsBuilder = new StringBuilder();
if (options.Lowercase.GetValueOrDefault() && options.MinLowercase.GetValueOrDefault() > 0)
if (options.Lowercase.GetValueOrDefault() && options.MinLowercase > 0)
{
for (int i = 0; i < options.MinLowercase.GetValueOrDefault(); i++)
for (int i = 0; i < options.MinLowercase; i++)
{
positionsBuilder.Append("l");
}
}
if (options.Uppercase.GetValueOrDefault() && options.MinUppercase.GetValueOrDefault() > 0)
if (options.Uppercase.GetValueOrDefault() && options.MinUppercase > 0)
{
for (int i = 0; i < options.MinUppercase.GetValueOrDefault(); i++)
for (int i = 0; i < options.MinUppercase; i++)
{
positionsBuilder.Append("u");
}
}
if (options.Number.GetValueOrDefault() && options.MinNumber.GetValueOrDefault() > 0)
if (options.Number.GetValueOrDefault() && options.MinNumber > 0)
{
for (int i = 0; i < options.MinNumber.GetValueOrDefault(); i++)
for (int i = 0; i < options.MinNumber; i++)
{
positionsBuilder.Append("n");
}
}
if (options.Special.GetValueOrDefault() && options.MinSpecial.GetValueOrDefault() > 0)
if (options.Special.GetValueOrDefault() && options.MinSpecial > 0)
{
for (int i = 0; i < options.MinSpecial.GetValueOrDefault(); i++)
for (int i = 0; i < options.MinSpecial; i++)
{
positionsBuilder.Append("s");
}
@ -92,68 +91,68 @@ namespace Bit.Core.Services
.OrderBy(a => _cryptoFunctionService.RandomNumber()).ToArray();
// Build out other character sets
var allCharSet = string.Empty;
var lowercaseCharSet = LowercaseCharSet;
var allCharSet = new StringBuilder();
var lowercaseCharSet = LOWERCASE_CHAR_SET;
if (options.AllowAmbiguousChar.GetValueOrDefault())
{
lowercaseCharSet = string.Concat(lowercaseCharSet, "l");
}
if (options.Lowercase.GetValueOrDefault())
{
allCharSet = string.Concat(allCharSet, lowercaseCharSet);
allCharSet.Append(lowercaseCharSet);
}
var uppercaseCharSet = UppercaseCharSet;
var uppercaseCharSet = UPPERCASE_CHAR_SET;
if (options.AllowAmbiguousChar.GetValueOrDefault())
{
uppercaseCharSet = string.Concat(uppercaseCharSet, "IO");
}
if (options.Uppercase.GetValueOrDefault())
{
allCharSet = string.Concat(allCharSet, uppercaseCharSet);
allCharSet.Append(uppercaseCharSet);
}
var numberCharSet = NumberCharSet;
var numberCharSet = NUMER_CHAR_SET;
if (options.AllowAmbiguousChar.GetValueOrDefault())
{
numberCharSet = string.Concat(numberCharSet, "01");
}
if (options.Number.GetValueOrDefault())
{
allCharSet = string.Concat(allCharSet, numberCharSet);
allCharSet.Append(numberCharSet);
}
var specialCharSet = SpecialCharSet;
if (options.Special.GetValueOrDefault())
{
allCharSet = string.Concat(allCharSet, specialCharSet);
allCharSet.Append(SPECIAL_CHAR_SET);
}
var password = new StringBuilder();
for (var i = 0; i < options.Length.GetValueOrDefault(); i++)
{
var positionChars = string.Empty;
var charSetOnCurrentPosition = string.Empty;
switch (positions[i])
{
case 'l':
positionChars = lowercaseCharSet;
charSetOnCurrentPosition = lowercaseCharSet;
break;
case 'u':
positionChars = uppercaseCharSet;
charSetOnCurrentPosition = uppercaseCharSet;
break;
case 'n':
positionChars = numberCharSet;
charSetOnCurrentPosition = numberCharSet;
break;
case 's':
positionChars = specialCharSet;
charSetOnCurrentPosition = SPECIAL_CHAR_SET;
break;
case 'a':
positionChars = allCharSet;
charSetOnCurrentPosition = allCharSet.ToString();
break;
}
var randomCharIndex = await _cryptoService.RandomNumberAsync(0, positionChars.Length - 1);
password.Append(positionChars[randomCharIndex]);
var randomCharIndex = await _cryptoService.RandomNumberAsync(0, charSetOnCurrentPosition.Length - 1);
password.Append(charSetOnCurrentPosition[randomCharIndex]);
}
return password.ToString();
@ -168,7 +167,7 @@ namespace Bit.Core.Services
public async Task<string> GeneratePassphraseAsync(PasswordGenerationOptions options)
{
options.Merge(_defaultOptions);
if (options.NumWords.GetValueOrDefault() <= 2)
if (options.NumWords <= 2)
{
options.NumWords = _defaultOptions.NumWords;
}
@ -221,179 +220,10 @@ namespace Bit.Core.Services
}
}
var (enforcedOptions, enforcedPolicyOptions) = await EnforcePasswordGeneratorPoliciesOnOptionsAsync(
_optionsCache);
_optionsCache = enforcedOptions;
return (_optionsCache, enforcedPolicyOptions);
}
var policyOptions = await _policyService.GetPasswordGeneratorPolicyOptionsAsync();
_optionsCache.EnforcePolicy(policyOptions);
public async Task<(PasswordGenerationOptions, PasswordGeneratorPolicyOptions)>
EnforcePasswordGeneratorPoliciesOnOptionsAsync(PasswordGenerationOptions options)
{
var enforcedPolicyOptions = await GetPasswordGeneratorPolicyOptions();
if (enforcedPolicyOptions != null)
{
if (options.Length < enforcedPolicyOptions.MinLength)
{
options.Length = enforcedPolicyOptions.MinLength;
}
if (enforcedPolicyOptions.UseUppercase)
{
options.Uppercase = true;
}
if (enforcedPolicyOptions.UseLowercase)
{
options.Lowercase = true;
}
if (enforcedPolicyOptions.UseNumbers)
{
options.Number = true;
}
if (options.MinNumber < enforcedPolicyOptions.NumberCount)
{
options.MinNumber = enforcedPolicyOptions.NumberCount;
}
if (enforcedPolicyOptions.UseSpecial)
{
options.Special = true;
}
if (options.MinSpecial < enforcedPolicyOptions.SpecialCount)
{
options.MinSpecial = enforcedPolicyOptions.SpecialCount;
}
// Must normalize these fields because the receiving call expects all options to pass the current rules
if (options.MinSpecial + options.MinNumber > options.Length)
{
options.MinSpecial = options.Length - options.MinNumber;
}
if (options.NumWords < enforcedPolicyOptions.MinNumberOfWords)
{
options.NumWords = enforcedPolicyOptions.MinNumberOfWords;
}
if (enforcedPolicyOptions.Capitalize)
{
options.Capitalize = true;
}
if (enforcedPolicyOptions.IncludeNumber)
{
options.IncludeNumber = true;
}
// Force default type if password/passphrase selected via policy
if (enforcedPolicyOptions.DefaultType == "password" || enforcedPolicyOptions.DefaultType == "passphrase")
{
options.Type = enforcedPolicyOptions.DefaultType;
}
}
else
{
// UI layer expects an instantiated object to prevent more explicit null checks
enforcedPolicyOptions = new PasswordGeneratorPolicyOptions();
}
return (options, 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 defaultType = GetPolicyString(currentPolicy, "defaultType");
if (defaultType != null && enforcedOptions.DefaultType != "password")
{
enforcedOptions.DefaultType = defaultType;
}
var minLength = GetPolicyInt(currentPolicy, "minLength");
if (minLength != null && (int)(long)minLength > enforcedOptions.MinLength)
{
enforcedOptions.MinLength = (int)(long)minLength;
}
var useUpper = GetPolicyBool(currentPolicy, "useUpper");
if (useUpper != null && (bool)useUpper)
{
enforcedOptions.UseUppercase = true;
}
var useLower = GetPolicyBool(currentPolicy, "useLower");
if (useLower != null && (bool)useLower)
{
enforcedOptions.UseLowercase = true;
}
var useNumbers = GetPolicyBool(currentPolicy, "useNumbers");
if (useNumbers != null && (bool)useNumbers)
{
enforcedOptions.UseNumbers = true;
}
var minNumbers = GetPolicyInt(currentPolicy, "minNumbers");
if (minNumbers != null && (int)(long)minNumbers > enforcedOptions.NumberCount)
{
enforcedOptions.NumberCount = (int)(long)minNumbers;
}
var useSpecial = GetPolicyBool(currentPolicy, "useSpecial");
if (useSpecial != null && (bool)useSpecial)
{
enforcedOptions.UseSpecial = true;
}
var minSpecial = GetPolicyInt(currentPolicy, "minSpecial");
if (minSpecial != null && (int)(long)minSpecial > enforcedOptions.SpecialCount)
{
enforcedOptions.SpecialCount = (int)(long)minSpecial;
}
var minNumberWords = GetPolicyInt(currentPolicy, "minNumberWords");
if (minNumberWords != null && (int)(long)minNumberWords > enforcedOptions.MinNumberOfWords)
{
enforcedOptions.MinNumberOfWords = (int)(long)minNumberWords;
}
var capitalize = GetPolicyBool(currentPolicy, "capitalize");
if (capitalize != null && (bool)capitalize)
{
enforcedOptions.Capitalize = true;
}
var includeNumber = GetPolicyBool(currentPolicy, "includeNumber");
if (includeNumber != null && (bool)includeNumber)
{
enforcedOptions.IncludeNumber = true;
}
}
return enforcedOptions;
return (_optionsCache, policyOptions ?? new PasswordGeneratorPolicyOptions());
}
public List<string> GetPasswordStrengthUserInput(string email)
@ -409,45 +239,6 @@ namespace Bit.Core.Services
return new List<string>(data);
}
private int? GetPolicyInt(Policy policy, string key)
{
if (policy.Data.ContainsKey(key))
{
var value = policy.Data[key];
if (value != null)
{
return (int)(long)value;
}
}
return null;
}
private bool? GetPolicyBool(Policy policy, string key)
{
if (policy.Data.ContainsKey(key))
{
var value = policy.Data[key];
if (value != null)
{
return (bool)value;
}
}
return null;
}
private string GetPolicyString(Policy policy, string key)
{
if (policy.Data.ContainsKey(key))
{
var value = policy.Data[key];
if (value != null)
{
return (string)value;
}
}
return null;
}
public async Task SaveOptionsAsync(PasswordGenerationOptions options)
{
await _stateService.SetPasswordGenerationOptionsAsync(options);
@ -485,7 +276,7 @@ namespace Bit.Core.Services
token.ThrowIfCancellationRequested();
currentHistory.Insert(0, new GeneratedPasswordHistory { Password = password, Date = DateTime.UtcNow });
// Remove old items.
if (currentHistory.Count > MaxPasswordsInHistory)
if (currentHistory.Count > MAX_PASSWORDS_IN_HISTORY)
{
currentHistory.RemoveAt(currentHistory.Count - 1);
}

View File

@ -17,11 +17,6 @@ namespace Bit.Core.Services
private IEnumerable<Policy> _policyCache;
public const string TIMEOUT_POLICY_MINUTES = "minutes";
public const string TIMEOUT_POLICY_ACTION = "action";
public const string TIMEOUT_POLICY_ACTION_LOCK = "lock";
public const string TIMEOUT_POLICY_ACTION_LOGOUT = "logOut";
public PolicyService(
IStateService stateService,
IOrganizationService organizationService)
@ -51,10 +46,8 @@ namespace Bit.Core.Services
{
return _policyCache.Where(p => p.Type == type).ToList();
}
else
{
return _policyCache;
}
return _policyCache;
}
public async Task Replace(Dictionary<string, PolicyData> policies, string userId = null)
@ -77,7 +70,7 @@ namespace Bit.Core.Services
public async Task UpdateVaultTimeoutFromPolicyAsync(Policy policy, string userId = null)
{
var policyTimeout = GetPolicyInt(policy, PolicyService.TIMEOUT_POLICY_MINUTES);
var policyTimeout = policy.GetInt(Policy.MINUTES_KEY);
if (policyTimeout != null)
{
var vaultTimeout = await _stateService.GetVaultTimeoutAsync(userId);
@ -92,11 +85,11 @@ namespace Bit.Core.Services
}
}
var policyAction = GetPolicyString(policy, PolicyService.TIMEOUT_POLICY_ACTION);
var policyAction = policy.GetString(Policy.ACTION_KEY);
if (!string.IsNullOrEmpty(policyAction))
{
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync(userId);
var action = policyAction == PolicyService.TIMEOUT_POLICY_ACTION_LOCK ? VaultTimeoutAction.Lock : VaultTimeoutAction.Logout;
var action = policyAction == Policy.ACTION_LOCK ? VaultTimeoutAction.Lock : VaultTimeoutAction.Logout;
if (vaultTimeoutAction != action)
{
await _stateService.SetVaultTimeoutActionAsync(action, userId);
@ -107,71 +100,63 @@ namespace Bit.Core.Services
public async Task<MasterPasswordPolicyOptions> GetMasterPasswordPolicyOptions(
IEnumerable<Policy> policies = null, string userId = null)
{
MasterPasswordPolicyOptions enforcedOptions = null;
if (policies == null)
{
policies = await GetAll(PolicyType.MasterPassword, userId);
if (policies == null)
{
return null;
}
}
else
{
policies = policies.Where(p => p.Type == PolicyType.MasterPassword);
}
if (policies == null || !policies.Any())
policies = policies.Where(p => p.Enabled && p.Data != null);
if (!policies.Any())
{
return enforcedOptions;
return null;
}
var enforcedOptions = new MasterPasswordPolicyOptions();
foreach (var currentPolicy in policies)
{
if (!currentPolicy.Enabled || currentPolicy.Data == null)
var minComplexity = currentPolicy.GetInt("minComplexity");
if (minComplexity > enforcedOptions.MinComplexity)
{
continue;
enforcedOptions.MinComplexity = minComplexity.Value;
}
if (enforcedOptions == null)
var minLength = currentPolicy.GetInt("minLength");
if (minLength > enforcedOptions.MinLength)
{
enforcedOptions = new MasterPasswordPolicyOptions();
enforcedOptions.MinLength = minLength.Value;
}
var minComplexity = GetPolicyInt(currentPolicy, "minComplexity");
if (minComplexity != null && (int)(long)minComplexity > enforcedOptions.MinComplexity)
{
enforcedOptions.MinComplexity = (int)(long)minComplexity;
}
var minLength = GetPolicyInt(currentPolicy, "minLength");
if (minLength != null && (int)(long)minLength > enforcedOptions.MinLength)
{
enforcedOptions.MinLength = (int)(long)minLength;
}
var requireUpper = GetPolicyBool(currentPolicy, "requireUpper");
if (requireUpper == true)
if (currentPolicy.GetBool("requireUpper") == true)
{
enforcedOptions.RequireUpper = true;
}
var requireLower = GetPolicyBool(currentPolicy, "requireLower");
if (requireLower == true)
if (currentPolicy.GetBool("requireLower") == true)
{
enforcedOptions.RequireLower = true;
}
var requireNumbers = GetPolicyBool(currentPolicy, "requireNumbers");
if (requireNumbers == true)
if (currentPolicy.GetBool("requireNumbers") == true)
{
enforcedOptions.RequireNumbers = true;
}
var requireSpecial = GetPolicyBool(currentPolicy, "requireSpecial");
if (requireSpecial == true)
if (currentPolicy.GetBool("requireSpecial") == true)
{
enforcedOptions.RequireSpecial = true;
}
var enforceOnLogin = GetPolicyBool(currentPolicy, "enforceOnLogin");
var enforceOnLogin = currentPolicy.GetBool("enforceOnLogin");
if (enforceOnLogin == true)
{
enforcedOptions.EnforceOnLogin = true;
@ -234,7 +219,7 @@ namespace Bit.Core.Services
var policy = policies.FirstOrDefault(p =>
p.OrganizationId == orgId && p.Type == PolicyType.ResetPassword && p.Enabled);
resetPasswordPolicyOptions.AutoEnrollEnabled = GetPolicyBool(policy, "autoEnrollEnabled") ?? false;
resetPasswordPolicyOptions.AutoEnrollEnabled = policy.GetBool("autoEnrollEnabled") ?? false;
return new Tuple<ResetPasswordPolicyOptions, bool>(resetPasswordPolicyOptions, policy != null);
}
@ -280,23 +265,6 @@ namespace Bit.Core.Services
return organization.isExemptFromPolicies;
}
public int? GetPolicyInt(Policy policy, string key)
{
if (policy.Data.ContainsKey(key))
{
var value = policy.Data[key];
if (value != null)
{
return (int)(long)value;
}
}
return null;
}
public string GetPolicyString(Policy policy, string key) =>
policy.Data.TryGetValue(key, out var val) ? val as string : null;
public async Task<bool> ShouldShowVaultFilterAsync()
{
var personalOwnershipPolicyApplies = await PolicyAppliesToUser(PolicyType.PersonalOwnership);
@ -309,19 +277,86 @@ namespace Bit.Core.Services
return organizations?.Any() ?? false;
}
private bool? GetPolicyBool(Policy policy, string key)
public async Task<PasswordGeneratorPolicyOptions> GetPasswordGeneratorPolicyOptionsAsync()
{
if (policy.Data.ContainsKey(key))
var policies = await GetAll(PolicyType.PasswordGenerator);
if (policies == null)
{
var value = policy.Data[key];
if (value != null)
return null;
}
var actualPolicies = policies.Where(p => p.Enabled && p.Data != null);
if (!actualPolicies.Any())
{
return null;
}
var enforcedOptions = new PasswordGeneratorPolicyOptions();
foreach (var currentPolicy in actualPolicies)
{
var defaultType = currentPolicy.GetString("defaultType");
if (defaultType != null && enforcedOptions.DefaultType != PasswordGenerationOptions.TYPE_PASSWORD)
{
return (bool)value;
enforcedOptions.DefaultType = defaultType;
}
var minLength = currentPolicy.GetInt("minLength");
if (minLength > enforcedOptions.MinLength)
{
enforcedOptions.MinLength = minLength.Value;
}
if (currentPolicy.GetBool("useUpper") == true)
{
enforcedOptions.UseUppercase = true;
}
if (currentPolicy.GetBool("useLower") == true)
{
enforcedOptions.UseLowercase = true;
}
if (currentPolicy.GetBool("useNumbers") == true)
{
enforcedOptions.UseNumbers = true;
}
var minNumbers = currentPolicy.GetInt("minNumbers");
if (minNumbers > enforcedOptions.NumberCount)
{
enforcedOptions.NumberCount = minNumbers.Value;
}
if (currentPolicy.GetBool("useSpecial") == true)
{
enforcedOptions.UseSpecial = true;
}
var minSpecial = currentPolicy.GetInt("minSpecial");
if (minSpecial > enforcedOptions.SpecialCount)
{
enforcedOptions.SpecialCount = minSpecial.Value;
}
var minNumberWords = currentPolicy.GetInt("minNumberWords");
if (minNumberWords > enforcedOptions.MinNumberOfWords)
{
enforcedOptions.MinNumberOfWords = minNumberWords.Value;
}
if (currentPolicy.GetBool("capitalize") == true)
{
enforcedOptions.Capitalize = true;
}
if (currentPolicy.GetBool("includeNumber") == true)
{
enforcedOptions.IncludeNumber = true;
}
}
return null;
return enforcedOptions;
}
}
}

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
namespace Bit.Core.Services
{

View File

@ -75,8 +75,7 @@ namespace Bit.Core.Utilities
messagingService.Send("logout", extras);
return Task.CompletedTask;
});
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService,
cryptoFunctionService, policyService);
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
var totpService = new TotpService(cryptoFunctionService);
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,