mirror of
https://github.com/bitwarden/android.git
synced 2024-12-21 00:31:58 +03:00
Password generator page. Password generation service. Tests. Renamed some settings constants.
This commit is contained in:
parent
cd4f1f4c2f
commit
55ed801fe7
17 changed files with 6955 additions and 98 deletions
|
@ -121,6 +121,7 @@ namespace Bit.Android
|
||||||
.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
|
.RegisterType<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
|
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
|
||||||
// Repositories
|
// Repositories
|
||||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
|
6203
src/Android/Resources/Resource.Designer.cs
generated
6203
src/Android/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface IPasswordGenerationService
|
||||||
|
{
|
||||||
|
string GeneratePassword();
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,6 +99,7 @@
|
||||||
<Compile Include="Models\Folder.cs" />
|
<Compile Include="Models\Folder.cs" />
|
||||||
<Compile Include="Models\Page\SettingsFolderPageModel.cs" />
|
<Compile Include="Models\Page\SettingsFolderPageModel.cs" />
|
||||||
<Compile Include="Models\Page\PinPageModel.cs" />
|
<Compile Include="Models\Page\PinPageModel.cs" />
|
||||||
|
<Compile Include="Models\Page\PasswordGeneratorPageModel.cs" />
|
||||||
<Compile Include="Models\PushNotification.cs" />
|
<Compile Include="Models\PushNotification.cs" />
|
||||||
<Compile Include="Models\Site.cs" />
|
<Compile Include="Models\Site.cs" />
|
||||||
<Compile Include="Models\Page\VaultViewSitePageModel.cs" />
|
<Compile Include="Models\Page\VaultViewSitePageModel.cs" />
|
||||||
|
@ -109,6 +110,8 @@
|
||||||
<Compile Include="Pages\MainPage.cs" />
|
<Compile Include="Pages\MainPage.cs" />
|
||||||
<Compile Include="Pages\Settings\SettingsEditFolderPage.cs" />
|
<Compile Include="Pages\Settings\SettingsEditFolderPage.cs" />
|
||||||
<Compile Include="Pages\Lock\LockFingerprintPage.cs" />
|
<Compile Include="Pages\Lock\LockFingerprintPage.cs" />
|
||||||
|
<Compile Include="Pages\Tools\ToolsPasswordGeneratorSettingsPage.cs" />
|
||||||
|
<Compile Include="Pages\Tools\ToolsPasswordGeneratorPage.cs" />
|
||||||
<Compile Include="Pages\Tools\ToolsPage.cs" />
|
<Compile Include="Pages\Tools\ToolsPage.cs" />
|
||||||
<Compile Include="Pages\Settings\SettingsSyncPage.cs" />
|
<Compile Include="Pages\Settings\SettingsSyncPage.cs" />
|
||||||
<Compile Include="Pages\Settings\SettingsPage.cs" />
|
<Compile Include="Pages\Settings\SettingsPage.cs" />
|
||||||
|
@ -144,6 +147,7 @@
|
||||||
<Compile Include="Abstractions\Services\ICryptoService.cs" />
|
<Compile Include="Abstractions\Services\ICryptoService.cs" />
|
||||||
<Compile Include="Abstractions\Services\IDatabaseService.cs" />
|
<Compile Include="Abstractions\Services\IDatabaseService.cs" />
|
||||||
<Compile Include="Abstractions\Services\ISyncService.cs" />
|
<Compile Include="Abstractions\Services\ISyncService.cs" />
|
||||||
|
<Compile Include="Abstractions\Services\IPasswordGenerationService.cs" />
|
||||||
<Compile Include="Services\SyncService.cs" />
|
<Compile Include="Services\SyncService.cs" />
|
||||||
<Compile Include="Services\SiteService.cs" />
|
<Compile Include="Services\SiteService.cs" />
|
||||||
<Compile Include="Services\AuthService.cs" />
|
<Compile Include="Services\AuthService.cs" />
|
||||||
|
@ -155,6 +159,7 @@
|
||||||
<Compile Include="Pages\Vault\VaultViewSitePage.cs" />
|
<Compile Include="Pages\Vault\VaultViewSitePage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultEditSitePage.cs" />
|
<Compile Include="Pages\Vault\VaultEditSitePage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultListSitesPage.cs" />
|
<Compile Include="Pages\Vault\VaultListSitesPage.cs" />
|
||||||
|
<Compile Include="Services\PasswordGenerationService.cs" />
|
||||||
<Compile Include="Utilities\Extentions.cs" />
|
<Compile Include="Utilities\Extentions.cs" />
|
||||||
<Compile Include="Utilities\ExtendedObservableCollection.cs" />
|
<Compile Include="Utilities\ExtendedObservableCollection.cs" />
|
||||||
<Compile Include="Utilities\ApiHttpClient.cs" />
|
<Compile Include="Utilities\ApiHttpClient.cs" />
|
||||||
|
|
|
@ -2,12 +2,21 @@
|
||||||
{
|
{
|
||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
public const string SettingFingerprintUnlockOn = "fingerprintUnlockOn";
|
public const string SettingFingerprintUnlockOn = "setting:fingerprintUnlockOn";
|
||||||
public const string SettingPinUnlockOn = "pinUnlockOn";
|
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
|
||||||
public const string SettingLockSeconds = "lockSeconds";
|
public const string SettingLockSeconds = "setting:lockSeconds";
|
||||||
public const string SettingLastBackgroundedDate = "lastBackgroundedDate";
|
public const string SettingLastBackgroundedDate = "lastBackgroundedDate";
|
||||||
|
|
||||||
public const string PushPromptShown = "initialPushPromptShown";
|
public const string PasswordGeneratorLength = "pwGenerator:length";
|
||||||
public const string PushLastRegistration = "lastPushRegistration";
|
public const string PasswordGeneratorUppercase = "pwGenerator:uppercase";
|
||||||
|
public const string PasswordGeneratorLowercase = "pwGenerator:lowercase";
|
||||||
|
public const string PasswordGeneratorNumbers = "pwGenerator:numbers";
|
||||||
|
public const string PasswordGeneratorMinNumbers = "pwGenerator:minNumbers";
|
||||||
|
public const string PasswordGeneratorSpecial = "pwGenerator:special";
|
||||||
|
public const string PasswordGeneratorMinSpecial = "pwGenerator:minSpecial";
|
||||||
|
public const string PasswordGeneratorAmbiguous = "pwGenerator:ambiguous";
|
||||||
|
|
||||||
|
public const string PushInitialPromptShown = "push:initialPromptShown";
|
||||||
|
public const string PushLastRegistrationDate = "push:lastRegistrationDate";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
src/App/Models/Page/PasswordGeneratorPageModel.cs
Normal file
35
src/App/Models/Page/PasswordGeneratorPageModel.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Bit.App.Models.Page
|
||||||
|
{
|
||||||
|
public class PasswordGeneratorPageModel : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private string _password = " ";
|
||||||
|
private string _length;
|
||||||
|
|
||||||
|
public PasswordGeneratorPageModel() { }
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
public string Password
|
||||||
|
{
|
||||||
|
get { return _password; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_password = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Password)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Length
|
||||||
|
{
|
||||||
|
get { return _length; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_length = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ namespace Bit.App.Pages
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
var generatorCell = new ToolsViewCell("Password Generator", "Automatically generate strong, unique passwords for your logins.", "refresh");
|
var generatorCell = new ToolsViewCell("Password Generator", "Automatically generate strong, unique passwords for your logins.", "refresh");
|
||||||
|
generatorCell.Tapped += GeneratorCell_Tapped;
|
||||||
var extensionCell = new ToolsViewCell("bitwarden App Extension", "Use bitwarden in Safari and other apps to auto-fill your logins.", "upload");
|
var extensionCell = new ToolsViewCell("bitwarden App Extension", "Use bitwarden in Safari and other apps to auto-fill your logins.", "upload");
|
||||||
var webCell = new ToolsViewCell("bitwarden Web Vault", "Manage your logins from any web browser with the bitwarden web vault.", "globe");
|
var webCell = new ToolsViewCell("bitwarden Web Vault", "Manage your logins from any web browser with the bitwarden web vault.", "globe");
|
||||||
webCell.Tapped += WebCell_Tapped;
|
webCell.Tapped += WebCell_Tapped;
|
||||||
|
@ -57,6 +58,11 @@ namespace Bit.App.Pages
|
||||||
Content = table;
|
Content = table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GeneratorCell_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Navigation.PushModalAsync(new ExtendedNavigationPage(new ToolsPasswordGeneratorPage()));
|
||||||
|
}
|
||||||
|
|
||||||
private void WebCell_Tapped(object sender, EventArgs e)
|
private void WebCell_Tapped(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Device.OpenUri(new Uri("https://vault.bitwarden.com"));
|
Device.OpenUri(new Uri("https://vault.bitwarden.com"));
|
||||||
|
|
210
src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs
Normal file
210
src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
using System;
|
||||||
|
using Acr.UserDialogs;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Models.Page;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Plugin.Settings.Abstractions;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using XLabs.Ioc;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class ToolsPasswordGeneratorPage : ExtendedContentPage
|
||||||
|
{
|
||||||
|
private readonly IUserDialogs _userDialogs;
|
||||||
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
|
private readonly ISettings _settings;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
|
public ToolsPasswordGeneratorPage()
|
||||||
|
{
|
||||||
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
|
_passwordGenerationService = Resolver.Resolve<IPasswordGenerationService>();
|
||||||
|
_settings = Resolver.Resolve<ISettings>();
|
||||||
|
_clipboardService = Resolver.Resolve<IClipboardService>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordGeneratorPageModel Model { get; private set; } = new PasswordGeneratorPageModel();
|
||||||
|
public Label Password { get; private set; }
|
||||||
|
public SliderViewCell SliderCell { get; private set; }
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
Password = new Label
|
||||||
|
{
|
||||||
|
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
|
||||||
|
Margin = new Thickness(15, 40, 15, 0),
|
||||||
|
HorizontalTextAlignment = TextAlignment.Center,
|
||||||
|
FontFamily = "Courier",
|
||||||
|
LineBreakMode = LineBreakMode.TailTruncation
|
||||||
|
};
|
||||||
|
|
||||||
|
var tgr = new TapGestureRecognizer();
|
||||||
|
tgr.Tapped += Tgr_Tapped;
|
||||||
|
Password.GestureRecognizers.Add(tgr);
|
||||||
|
Password.SetBinding<PasswordGeneratorPageModel>(Label.TextProperty, m => m.Password);
|
||||||
|
|
||||||
|
SliderCell = new SliderViewCell(this, _passwordGenerationService, _settings);
|
||||||
|
var settingsCell = new ExtendedTextCell { Text = "More Settings", ShowDisclousure = true };
|
||||||
|
settingsCell.Tapped += SettingsCell_Tapped;
|
||||||
|
|
||||||
|
var buttonColor = Color.FromHex("3c8dbc");
|
||||||
|
var regenerateCell = new ExtendedTextCell { Text = "Regenerate Password", TextColor = buttonColor };
|
||||||
|
regenerateCell.Tapped += RegenerateCell_Tapped; ;
|
||||||
|
var copyCell = new ExtendedTextCell { Text = "Copy Password", TextColor = buttonColor };
|
||||||
|
copyCell.Tapped += CopyCell_Tapped;
|
||||||
|
var saveCell = new ExtendedTextCell { Text = "Save Password", TextColor = buttonColor };
|
||||||
|
saveCell.Tapped += SaveCell_Tapped;
|
||||||
|
|
||||||
|
var table = new ExtendedTableView
|
||||||
|
{
|
||||||
|
EnableScrolling = false,
|
||||||
|
Intent = TableIntent.Menu,
|
||||||
|
HasUnevenRows = true,
|
||||||
|
Root = new TableRoot
|
||||||
|
{
|
||||||
|
new TableSection
|
||||||
|
{
|
||||||
|
SliderCell,
|
||||||
|
settingsCell
|
||||||
|
},
|
||||||
|
new TableSection
|
||||||
|
{
|
||||||
|
regenerateCell,
|
||||||
|
copyCell
|
||||||
|
},
|
||||||
|
new TableSection
|
||||||
|
{
|
||||||
|
saveCell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(Device.OS == TargetPlatform.iOS)
|
||||||
|
{
|
||||||
|
table.RowHeight = -1;
|
||||||
|
table.EstimatedRowHeight = 44;
|
||||||
|
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var stackLayout = new StackLayout
|
||||||
|
{
|
||||||
|
Orientation = StackOrientation.Vertical,
|
||||||
|
Children = { Password, table }
|
||||||
|
};
|
||||||
|
|
||||||
|
var scrollView = new ScrollView
|
||||||
|
{
|
||||||
|
Content = stackLayout,
|
||||||
|
Orientation = ScrollOrientation.Vertical
|
||||||
|
};
|
||||||
|
|
||||||
|
Title = "Generate Password";
|
||||||
|
Content = scrollView;
|
||||||
|
BindingContext = Model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Tgr_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
CopyPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAppearing()
|
||||||
|
{
|
||||||
|
Model.Password = _passwordGenerationService.GeneratePassword();
|
||||||
|
Model.Length = _settings.GetValueOrDefault(Constants.PasswordGeneratorLength, 10).ToString();
|
||||||
|
base.OnAppearing();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegenerateCell_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Model.Password = _passwordGenerationService.GeneratePassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveCell_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyCell_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
CopyPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SettingsCell_Tapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Navigation.PushAsync(new ToolsPasswordGeneratorSettingsPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyPassword()
|
||||||
|
{
|
||||||
|
_clipboardService.CopyToClipboard(Password.Text);
|
||||||
|
_userDialogs.SuccessToast(string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SliderViewCell : ExtendedViewCell
|
||||||
|
{
|
||||||
|
private readonly ToolsPasswordGeneratorPage _page;
|
||||||
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
|
private readonly ISettings _settings;
|
||||||
|
|
||||||
|
public Label Value { get; set; }
|
||||||
|
public Slider LengthSlider { get; set; }
|
||||||
|
|
||||||
|
public SliderViewCell(
|
||||||
|
ToolsPasswordGeneratorPage page,
|
||||||
|
IPasswordGenerationService passwordGenerationService,
|
||||||
|
ISettings settings)
|
||||||
|
{
|
||||||
|
_page = page;
|
||||||
|
_passwordGenerationService = passwordGenerationService;
|
||||||
|
_settings = settings;
|
||||||
|
|
||||||
|
var label = new Label
|
||||||
|
{
|
||||||
|
Text = "Length",
|
||||||
|
HorizontalOptions = LayoutOptions.Start,
|
||||||
|
VerticalOptions = LayoutOptions.CenterAndExpand
|
||||||
|
};
|
||||||
|
|
||||||
|
LengthSlider = new Slider(5, 64, _settings.GetValueOrDefault(Constants.PasswordGeneratorLength, 10))
|
||||||
|
{
|
||||||
|
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||||
|
VerticalOptions = LayoutOptions.CenterAndExpand
|
||||||
|
};
|
||||||
|
|
||||||
|
Value = new Label
|
||||||
|
{
|
||||||
|
HorizontalOptions = LayoutOptions.End,
|
||||||
|
VerticalOptions = LayoutOptions.CenterAndExpand,
|
||||||
|
Style = (Style)Application.Current.Resources["text-muted"]
|
||||||
|
};
|
||||||
|
|
||||||
|
Value.SetBinding<PasswordGeneratorPageModel>(Label.TextProperty, m => m.Length);
|
||||||
|
|
||||||
|
LengthSlider.ValueChanged += Slider_ValueChanged;
|
||||||
|
|
||||||
|
var stackLayout = new StackLayout
|
||||||
|
{
|
||||||
|
Orientation = StackOrientation.Horizontal,
|
||||||
|
Spacing = 15,
|
||||||
|
Children = { label, LengthSlider, Value },
|
||||||
|
Padding = new Thickness(15)
|
||||||
|
};
|
||||||
|
|
||||||
|
View = stackLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Slider_ValueChanged(object sender, ValueChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var length = Convert.ToInt32(LengthSlider.Value);
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorLength, length);
|
||||||
|
_page.Model.Length = length.ToString();
|
||||||
|
_page.Model.Password = _passwordGenerationService.GeneratePassword();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
192
src/App/Pages/Tools/ToolsPasswordGeneratorSettingsPage.cs
Normal file
192
src/App/Pages/Tools/ToolsPasswordGeneratorSettingsPage.cs
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Acr.UserDialogs;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Plugin.Connectivity.Abstractions;
|
||||||
|
using Plugin.Settings.Abstractions;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using XLabs.Ioc;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class ToolsPasswordGeneratorSettingsPage : ExtendedContentPage
|
||||||
|
{
|
||||||
|
private readonly IUserDialogs _userDialogs;
|
||||||
|
private readonly ISettings _settings;
|
||||||
|
|
||||||
|
public ToolsPasswordGeneratorSettingsPage()
|
||||||
|
{
|
||||||
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
|
_settings = Resolver.Resolve<ISettings>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtendedSwitchCell UppercaseCell { get; set; }
|
||||||
|
public ExtendedSwitchCell LowercaseCell { get; set; }
|
||||||
|
public ExtendedSwitchCell SpecialCell { get; set; }
|
||||||
|
public ExtendedSwitchCell NumbersCell { get; set; }
|
||||||
|
public ExtendedSwitchCell AvoidAmbiguousCell { get; set; }
|
||||||
|
public EntryCell SpecialMinCell { get; set; }
|
||||||
|
public EntryCell NumbersMinCell { get; set; }
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
UppercaseCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = "A-Z",
|
||||||
|
On = _settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, true)
|
||||||
|
};
|
||||||
|
UppercaseCell.OnChanged += UppercaseCell_OnChanged;
|
||||||
|
|
||||||
|
LowercaseCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = "a-z",
|
||||||
|
On = _settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, true)
|
||||||
|
};
|
||||||
|
LowercaseCell.OnChanged += LowercaseCell_OnChanged;
|
||||||
|
|
||||||
|
SpecialCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = "!@#$%^&*",
|
||||||
|
On = _settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, true)
|
||||||
|
};
|
||||||
|
SpecialCell.OnChanged += SpecialCell_OnChanged;
|
||||||
|
|
||||||
|
NumbersCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = "0-9",
|
||||||
|
On = _settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, true)
|
||||||
|
};
|
||||||
|
NumbersCell.OnChanged += NumbersCell_OnChanged;
|
||||||
|
|
||||||
|
AvoidAmbiguousCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = "Avoid Ambiguous Characters",
|
||||||
|
On = !_settings.GetValueOrDefault(Constants.PasswordGeneratorAmbiguous, false)
|
||||||
|
};
|
||||||
|
AvoidAmbiguousCell.OnChanged += AvoidAmbiguousCell_OnChanged; ;
|
||||||
|
|
||||||
|
NumbersMinCell = new EntryCell
|
||||||
|
{
|
||||||
|
Label = "Minimum Numbers",
|
||||||
|
Text = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, 1).ToString(),
|
||||||
|
Keyboard = Keyboard.Numeric
|
||||||
|
};
|
||||||
|
|
||||||
|
SpecialMinCell = new EntryCell
|
||||||
|
{
|
||||||
|
Label = "Minimum Special",
|
||||||
|
Text = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, 1).ToString(),
|
||||||
|
Keyboard = Keyboard.Numeric
|
||||||
|
};
|
||||||
|
|
||||||
|
var table = new ExtendedTableView
|
||||||
|
{
|
||||||
|
EnableScrolling = true,
|
||||||
|
Intent = TableIntent.Menu,
|
||||||
|
HasUnevenRows = true,
|
||||||
|
EnableSelection = false,
|
||||||
|
Root = new TableRoot
|
||||||
|
{
|
||||||
|
new TableSection
|
||||||
|
{
|
||||||
|
UppercaseCell,
|
||||||
|
LowercaseCell,
|
||||||
|
NumbersCell,
|
||||||
|
SpecialCell
|
||||||
|
},
|
||||||
|
new TableSection
|
||||||
|
{
|
||||||
|
NumbersMinCell,
|
||||||
|
SpecialMinCell
|
||||||
|
},
|
||||||
|
new TableSection
|
||||||
|
{
|
||||||
|
AvoidAmbiguousCell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(Device.OS == TargetPlatform.iOS)
|
||||||
|
{
|
||||||
|
table.RowHeight = -1;
|
||||||
|
table.EstimatedRowHeight = 44;
|
||||||
|
}
|
||||||
|
|
||||||
|
Title = "Settings";
|
||||||
|
Content = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisappearing()
|
||||||
|
{
|
||||||
|
int minNumber;
|
||||||
|
if(int.TryParse(NumbersMinCell.Text, out minNumber))
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorMinNumbers, minNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
int minSpecial;
|
||||||
|
if(int.TryParse(SpecialMinCell.Text, out minSpecial))
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorMinSpecial, minSpecial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AvoidAmbiguousCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorAmbiguous, !AvoidAmbiguousCell.On);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NumbersCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorNumbers, NumbersCell.On);
|
||||||
|
|
||||||
|
if(InvalidState())
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorLowercase, true);
|
||||||
|
LowercaseCell.On = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpecialCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorSpecial, SpecialCell.On);
|
||||||
|
|
||||||
|
if(InvalidState())
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorLowercase, true);
|
||||||
|
LowercaseCell.On = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LowercaseCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorLowercase, LowercaseCell.On);
|
||||||
|
|
||||||
|
if(InvalidState())
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorUppercase, true);
|
||||||
|
UppercaseCell.On = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UppercaseCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorUppercase, UppercaseCell.On);
|
||||||
|
|
||||||
|
if(InvalidState())
|
||||||
|
{
|
||||||
|
_settings.AddOrUpdateValue(Constants.PasswordGeneratorLowercase, true);
|
||||||
|
LowercaseCell.On = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool InvalidState()
|
||||||
|
{
|
||||||
|
return !LowercaseCell.On && !UppercaseCell.On && !NumbersCell.On && !SpecialCell.On;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,17 +82,17 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
if(_connectivity.IsConnected && Device.OS == TargetPlatform.iOS && !_favorites)
|
if(_connectivity.IsConnected && Device.OS == TargetPlatform.iOS && !_favorites)
|
||||||
{
|
{
|
||||||
var pushPromptShow = _settings.GetValueOrDefault<bool>(Constants.PushPromptShown);
|
var pushPromptShow = _settings.GetValueOrDefault<bool>(Constants.PushInitialPromptShown);
|
||||||
if(!pushPromptShow)
|
if(!pushPromptShow)
|
||||||
{
|
{
|
||||||
_settings.AddOrUpdateValue(Constants.PushPromptShown, true);
|
_settings.AddOrUpdateValue(Constants.PushInitialPromptShown, true);
|
||||||
await _userDialogs.AlertAsync(@"bitwarden keeps your vault automatically synced by using push notifications.
|
await _userDialogs.AlertAsync(@"bitwarden keeps your vault automatically synced by using push notifications.
|
||||||
For the best possible experience, please select ""Ok"" on the following prompt when asked to enable push notifications.",
|
For the best possible experience, please select ""Ok"" on the following prompt when asked to enable push notifications.",
|
||||||
"Enable Automatic Syncing", "Ok, got it!");
|
"Enable Automatic Syncing", "Ok, got it!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check push registration once per day
|
// Check push registration once per day
|
||||||
var lastPushRegistration = _settings.GetValueOrDefault<DateTime?>(Constants.PushLastRegistration);
|
var lastPushRegistration = _settings.GetValueOrDefault<DateTime?>(Constants.PushLastRegistrationDate);
|
||||||
if(!pushPromptShow || !lastPushRegistration.HasValue || (DateTime.UtcNow - lastPushRegistration) > TimeSpan.FromDays(1))
|
if(!pushPromptShow || !lastPushRegistration.HasValue || (DateTime.UtcNow - lastPushRegistration) > TimeSpan.FromDays(1))
|
||||||
{
|
{
|
||||||
_pushNotification.Register();
|
_pushNotification.Register();
|
||||||
|
|
168
src/App/Services/PasswordGenerationService.cs
Normal file
168
src/App/Services/PasswordGenerationService.cs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Plugin.Settings.Abstractions;
|
||||||
|
|
||||||
|
namespace Bit.App.Services
|
||||||
|
{
|
||||||
|
public class PasswordGenerationService : IPasswordGenerationService
|
||||||
|
{
|
||||||
|
private readonly ISettings _settings;
|
||||||
|
private Random _random = new Random();
|
||||||
|
|
||||||
|
public PasswordGenerationService(ISettings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GeneratePassword()
|
||||||
|
{
|
||||||
|
int minUppercase = 1,
|
||||||
|
minLowercase = 1,
|
||||||
|
minNumbers = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, 1),
|
||||||
|
minSpecial = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, 1),
|
||||||
|
length = _settings.GetValueOrDefault(Constants.PasswordGeneratorLength, 10);
|
||||||
|
|
||||||
|
bool uppercase = _settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, true),
|
||||||
|
lowercase = _settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, true),
|
||||||
|
numbers = _settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, true),
|
||||||
|
special = _settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, true),
|
||||||
|
ambiguous = _settings.GetValueOrDefault(Constants.PasswordGeneratorAmbiguous, false);
|
||||||
|
|
||||||
|
// Sanitize
|
||||||
|
if(uppercase && minUppercase < 0)
|
||||||
|
{
|
||||||
|
minUppercase = 1;
|
||||||
|
}
|
||||||
|
if(lowercase && minLowercase < 0)
|
||||||
|
{
|
||||||
|
minLowercase = 1;
|
||||||
|
}
|
||||||
|
if(numbers && minNumbers < 0)
|
||||||
|
{
|
||||||
|
minNumbers = 1;
|
||||||
|
}
|
||||||
|
if(special && minSpecial < 0)
|
||||||
|
{
|
||||||
|
minSpecial = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(length < 1)
|
||||||
|
{
|
||||||
|
length = 10;
|
||||||
|
}
|
||||||
|
var minLength = minUppercase + minLowercase + minNumbers + minSpecial;
|
||||||
|
if(length < minLength)
|
||||||
|
{
|
||||||
|
length = minLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
var positionsBuilder = new StringBuilder();
|
||||||
|
if(lowercase && minLowercase > 0)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < minLowercase; i++)
|
||||||
|
{
|
||||||
|
positionsBuilder.Append("l");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(uppercase && minUppercase > 0)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < minUppercase; i++)
|
||||||
|
{
|
||||||
|
positionsBuilder.Append("u");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(numbers && minNumbers > 0)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < minNumbers; i++)
|
||||||
|
{
|
||||||
|
positionsBuilder.Append("n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(special && minSpecial > 0)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < minSpecial; i++)
|
||||||
|
{
|
||||||
|
positionsBuilder.Append("s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(positionsBuilder.Length < length)
|
||||||
|
{
|
||||||
|
positionsBuilder.Append("a");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle
|
||||||
|
var positions = positionsBuilder.ToString().ToCharArray().OrderBy(a => _random.Next()).ToArray();
|
||||||
|
|
||||||
|
// Build out other character sets
|
||||||
|
var allCharSet = string.Empty;
|
||||||
|
|
||||||
|
var lowercaseCharSet = "abcdefghijkmnopqrstuvwxyz";
|
||||||
|
if(ambiguous)
|
||||||
|
{
|
||||||
|
lowercaseCharSet = string.Concat(lowercaseCharSet, "l");
|
||||||
|
}
|
||||||
|
if(lowercase)
|
||||||
|
{
|
||||||
|
allCharSet = string.Concat(allCharSet, lowercaseCharSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
var uppercaseCharSet = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
|
||||||
|
if(ambiguous)
|
||||||
|
{
|
||||||
|
uppercaseCharSet = string.Concat(uppercaseCharSet, "O");
|
||||||
|
}
|
||||||
|
if(uppercase)
|
||||||
|
{
|
||||||
|
allCharSet = string.Concat(allCharSet, uppercaseCharSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
var numberCharSet = "23456789";
|
||||||
|
if(ambiguous)
|
||||||
|
{
|
||||||
|
numberCharSet = string.Concat(numberCharSet, "01");
|
||||||
|
}
|
||||||
|
if(numbers)
|
||||||
|
{
|
||||||
|
allCharSet = string.Concat(allCharSet, numberCharSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
var specialCharSet = "!@#$%^&*";
|
||||||
|
if(special)
|
||||||
|
{
|
||||||
|
allCharSet = string.Concat(allCharSet, specialCharSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
var password = new StringBuilder();
|
||||||
|
for(var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
string positionChars = string.Empty;
|
||||||
|
switch(positions[i])
|
||||||
|
{
|
||||||
|
case 'l':
|
||||||
|
positionChars = lowercaseCharSet;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
positionChars = uppercaseCharSet;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
positionChars = numberCharSet;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
positionChars = specialCharSet;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
positionChars = allCharSet;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var randomCharIndex = _random.Next(0, positionChars.Length - 1);
|
||||||
|
password.Append(positionChars[randomCharIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return password.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,7 +76,7 @@ namespace Bit.App.Services
|
||||||
if(response.Succeeded)
|
if(response.Succeeded)
|
||||||
{
|
{
|
||||||
Debug.WriteLine("Registered device with server.");
|
Debug.WriteLine("Registered device with server.");
|
||||||
_settings.AddOrUpdateValue(Constants.PushLastRegistration, DateTime.UtcNow);
|
_settings.AddOrUpdateValue(Constants.PushLastRegistrationDate, DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -87,7 +87,7 @@ namespace Bit.App.Services
|
||||||
public void OnUnregistered(DeviceType deviceType)
|
public void OnUnregistered(DeviceType deviceType)
|
||||||
{
|
{
|
||||||
Debug.WriteLine("Push Notification - Device Unnregistered");
|
Debug.WriteLine("Push Notification - Device Unnregistered");
|
||||||
_settings.Remove(Constants.PushLastRegistration);
|
_settings.Remove(Constants.PushLastRegistrationDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnError(string message, DeviceType deviceType)
|
public void OnError(string message, DeviceType deviceType)
|
||||||
|
|
|
@ -94,6 +94,7 @@ namespace Bit.iOS.Extension
|
||||||
.RegisterType<IFolderService, FolderService>(new ContainerControlledLifetimeManager())
|
.RegisterType<IFolderService, FolderService>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<ISiteService, SiteService>(new ContainerControlledLifetimeManager())
|
.RegisterType<ISiteService, SiteService>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<ISyncService, SyncService>(new ContainerControlledLifetimeManager())
|
.RegisterType<ISyncService, SyncService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
|
||||||
//.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
//.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
||||||
// Repositories
|
// Repositories
|
||||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
|
|
@ -185,6 +185,7 @@ namespace Bit.iOS
|
||||||
.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
|
.RegisterType<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
|
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
|
||||||
// Repositories
|
// Repositories
|
||||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
|
|
@ -42,6 +42,14 @@
|
||||||
<HintPath>..\..\packages\NSubstitute.1.10.0.0\lib\net45\NSubstitute.dll</HintPath>
|
<HintPath>..\..\packages\NSubstitute.1.10.0.0\lib\net45\NSubstitute.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Plugin.Settings, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\net45\Plugin.Settings.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Plugin.Settings.Abstractions, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\net45\Plugin.Settings.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
@ -69,6 +77,7 @@
|
||||||
<Otherwise />
|
<Otherwise />
|
||||||
</Choose>
|
</Choose>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="PasswordGeneratorServiceTests.cs" />
|
||||||
<Compile Include="CryptoServiceTests.cs" />
|
<Compile Include="CryptoServiceTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
177
test/App.Test/PasswordGeneratorServiceTests.cs
Normal file
177
test/App.Test/PasswordGeneratorServiceTests.cs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Bit.App.Services;
|
||||||
|
using NSubstitute;
|
||||||
|
using Plugin.Settings.Abstractions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.App.Test
|
||||||
|
{
|
||||||
|
public class PasswordGeneratorServiceTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectLength()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorLength, Arg.Any<int>()).Returns(25);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Length == 25);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectMinNumbers()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, Arg.Any<int>()).Returns(25);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(char.IsDigit) >= 25);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectMinSpecial()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, Arg.Any<int>()).Returns(25);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(c => !char.IsLetterOrDigit(c)) >= 25);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectNoUppercase()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, Arg.Any<bool>()).Returns(false);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(char.IsUpper) == 0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectUppercase()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, Arg.Any<bool>()).Returns(true);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, Arg.Any<bool>()).Returns(false);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, Arg.Any<bool>()).Returns(false);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, Arg.Any<bool>()).Returns(false);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(char.IsUpper) == 50);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectNoLowercase()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, Arg.Any<bool>()).Returns(false);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(char.IsLower) == 0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectLowercase()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, Arg.Any<bool>()).Returns(false);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, Arg.Any<bool>()).Returns(true);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, Arg.Any<bool>()).Returns(false);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, Arg.Any<bool>()).Returns(false);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(char.IsLower) == 50);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectMixed()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(password.Count(char.IsLower) >= 1);
|
||||||
|
Assert.True(password.Count(char.IsDigit) >= 1);
|
||||||
|
Assert.True(password.Count(char.IsUpper) >= 1);
|
||||||
|
Assert.True(password.Count(c => !char.IsLetterOrDigit(c)) >= 1);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GeneratesCorrectNoAmbiguous()
|
||||||
|
{
|
||||||
|
var settings = SetupDefaultSettings();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorAmbiguous, Arg.Any<bool>()).Returns(false);
|
||||||
|
|
||||||
|
var service = new PasswordGenerationService(settings);
|
||||||
|
int i = 0;
|
||||||
|
while(i < 100)
|
||||||
|
{
|
||||||
|
var password = service.GeneratePassword();
|
||||||
|
Assert.True(!password.Any(c => c == '1' || c == 'l' || c == '0' || c == 'O'));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISettings SetupDefaultSettings()
|
||||||
|
{
|
||||||
|
var settings = Substitute.For<ISettings>();
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorLength, Arg.Any<int>()).Returns(50);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, Arg.Any<int>()).Returns(1);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, Arg.Any<int>()).Returns(1);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, Arg.Any<bool>()).Returns(true);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, Arg.Any<bool>()).Returns(true);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, Arg.Any<bool>()).Returns(true);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, Arg.Any<bool>()).Returns(true);
|
||||||
|
settings.GetValueOrDefault(Constants.PasswordGeneratorAmbiguous, Arg.Any<bool>()).Returns(false);
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NSubstitute" version="1.10.0.0" targetFramework="net46" />
|
<package id="NSubstitute" version="1.10.0.0" targetFramework="net46" />
|
||||||
|
<package id="Xam.Plugins.Settings" version="2.1.0" targetFramework="net46" />
|
||||||
<package id="xunit" version="2.1.0" targetFramework="net46" />
|
<package id="xunit" version="2.1.0" targetFramework="net46" />
|
||||||
<package id="xunit.abstractions" version="2.0.0" targetFramework="net46" />
|
<package id="xunit.abstractions" version="2.0.0" targetFramework="net46" />
|
||||||
<package id="xunit.assert" version="2.1.0" targetFramework="net46" />
|
<package id="xunit.assert" version="2.1.0" targetFramework="net46" />
|
||||||
|
|
Loading…
Reference in a new issue