using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Bit.App.Abstractions; using Bit.App.Resources; using Bit.Core.Abstractions; using Bit.Core.Models.Domain; using Bit.Core.Utilities; using Xamarin.Essentials; namespace Bit.App.Pages { public class BaseChangePasswordViewModel : BaseViewModel { protected readonly IPlatformUtilsService _platformUtilsService; protected readonly IStateService _stateService; protected readonly IPolicyService _policyService; protected readonly IPasswordGenerationService _passwordGenerationService; protected readonly II18nService _i18nService; protected readonly ICryptoService _cryptoService; protected readonly IDeviceActionService _deviceActionService; protected readonly IApiService _apiService; protected readonly ISyncService _syncService; private bool _showPassword; private bool _isPolicyInEffect; private string _policySummary; private MasterPasswordPolicyOptions _policy; protected BaseChangePasswordViewModel() { _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _stateService = ServiceContainer.Resolve("stateService"); _policyService = ServiceContainer.Resolve("policyService"); _passwordGenerationService = ServiceContainer.Resolve("passwordGenerationService"); _i18nService = ServiceContainer.Resolve("i18nService"); _cryptoService = ServiceContainer.Resolve("cryptoService"); _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _apiService = ServiceContainer.Resolve("apiService"); _syncService = ServiceContainer.Resolve("syncService"); } public bool ShowPassword { get => _showPassword; set => SetProperty(ref _showPassword, value, additionalPropertyNames: new[] { nameof(ShowPasswordIcon) }); } public bool IsPolicyInEffect { get => _isPolicyInEffect; set => SetProperty(ref _isPolicyInEffect, value); } public string PolicySummary { get => _policySummary; set => SetProperty(ref _policySummary, value); } public MasterPasswordPolicyOptions Policy { get => _policy; set => SetProperty(ref _policy, value); } public string ShowPasswordIcon => ShowPassword ? "" : ""; public string MasterPassword { get; set; } public string ConfirmMasterPassword { get; set; } public string Hint { get; set; } public async Task InitAsync(bool forceSync = false) { if (forceSync) { var task = Task.Run(async () => await _syncService.FullSyncAsync(true)); await task.ContinueWith(async (t) => await CheckPasswordPolicy()); } else { await CheckPasswordPolicy(); } } private async Task CheckPasswordPolicy() { Policy = await _policyService.GetMasterPasswordPolicyOptions(); IsPolicyInEffect = Policy?.InEffect() ?? false; if (!IsPolicyInEffect) { return; } var bullet = "\n" + "".PadLeft(4) + "\u2022 "; var sb = new StringBuilder(); sb.Append(_i18nService.T("MasterPasswordPolicyInEffect")); if (Policy.MinComplexity > 0) { sb.Append(bullet) .Append(string.Format(_i18nService.T("PolicyInEffectMinComplexity"), Policy.MinComplexity)); } if (Policy.MinLength > 0) { sb.Append(bullet).Append(string.Format(_i18nService.T("PolicyInEffectMinLength"), Policy.MinLength)); } if (Policy.RequireUpper) { sb.Append(bullet).Append(_i18nService.T("PolicyInEffectUppercase")); } if (Policy.RequireLower) { sb.Append(bullet).Append(_i18nService.T("PolicyInEffectLowercase")); } if (Policy.RequireNumbers) { sb.Append(bullet).Append(_i18nService.T("PolicyInEffectNumbers")); } if (Policy.RequireSpecial) { sb.Append(bullet).Append(string.Format(_i18nService.T("PolicyInEffectSpecial"), "!@#$%^&*")); } PolicySummary = sb.ToString(); } protected async Task ValidateMasterPasswordAsync() { if (Connectivity.NetworkAccess == NetworkAccess.None) { await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage, AppResources.InternetConnectionRequiredTitle, AppResources.Ok); return false; } if (string.IsNullOrWhiteSpace(MasterPassword)) { await _platformUtilsService.ShowDialogAsync( string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), AppResources.AnErrorHasOccurred, AppResources.Ok); return false; } if (IsPolicyInEffect) { var userInput = await GetPasswordStrengthUserInput(); var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInput); if (!await _policyService.EvaluateMasterPassword(passwordStrength.Score, MasterPassword, Policy)) { await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordPolicyValidationMessage, AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok); return false; } } else { if (MasterPassword.Length < 8) { await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage, AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok); return false; } } if (MasterPassword != ConfirmMasterPassword) { await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordConfirmationValMessage, AppResources.AnErrorHasOccurred, AppResources.Ok); return false; } return true; } private async Task> GetPasswordStrengthUserInput() { var email = await _stateService.GetEmailAsync(); List userInput = null; var atPosition = email.IndexOf('@'); if (atPosition > -1) { var rx = new Regex("/[^A-Za-z0-9]/", RegexOptions.Compiled); var data = rx.Split(email.Substring(0, atPosition).Trim().ToLower()); userInput = new List(data); } return userInput; } } }