mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +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<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
|
||||
// Repositories
|
||||
.RegisterType<IFolderRepository, FolderRepository>(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\Page\SettingsFolderPageModel.cs" />
|
||||
<Compile Include="Models\Page\PinPageModel.cs" />
|
||||
<Compile Include="Models\Page\PasswordGeneratorPageModel.cs" />
|
||||
<Compile Include="Models\PushNotification.cs" />
|
||||
<Compile Include="Models\Site.cs" />
|
||||
<Compile Include="Models\Page\VaultViewSitePageModel.cs" />
|
||||
|
@ -109,6 +110,8 @@
|
|||
<Compile Include="Pages\MainPage.cs" />
|
||||
<Compile Include="Pages\Settings\SettingsEditFolderPage.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\Settings\SettingsSyncPage.cs" />
|
||||
<Compile Include="Pages\Settings\SettingsPage.cs" />
|
||||
|
@ -144,6 +147,7 @@
|
|||
<Compile Include="Abstractions\Services\ICryptoService.cs" />
|
||||
<Compile Include="Abstractions\Services\IDatabaseService.cs" />
|
||||
<Compile Include="Abstractions\Services\ISyncService.cs" />
|
||||
<Compile Include="Abstractions\Services\IPasswordGenerationService.cs" />
|
||||
<Compile Include="Services\SyncService.cs" />
|
||||
<Compile Include="Services\SiteService.cs" />
|
||||
<Compile Include="Services\AuthService.cs" />
|
||||
|
@ -155,6 +159,7 @@
|
|||
<Compile Include="Pages\Vault\VaultViewSitePage.cs" />
|
||||
<Compile Include="Pages\Vault\VaultEditSitePage.cs" />
|
||||
<Compile Include="Pages\Vault\VaultListSitesPage.cs" />
|
||||
<Compile Include="Services\PasswordGenerationService.cs" />
|
||||
<Compile Include="Utilities\Extentions.cs" />
|
||||
<Compile Include="Utilities\ExtendedObservableCollection.cs" />
|
||||
<Compile Include="Utilities\ApiHttpClient.cs" />
|
||||
|
|
|
@ -2,12 +2,21 @@
|
|||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const string SettingFingerprintUnlockOn = "fingerprintUnlockOn";
|
||||
public const string SettingPinUnlockOn = "pinUnlockOn";
|
||||
public const string SettingLockSeconds = "lockSeconds";
|
||||
public const string SettingFingerprintUnlockOn = "setting:fingerprintUnlockOn";
|
||||
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
|
||||
public const string SettingLockSeconds = "setting:lockSeconds";
|
||||
public const string SettingLastBackgroundedDate = "lastBackgroundedDate";
|
||||
|
||||
public const string PushPromptShown = "initialPushPromptShown";
|
||||
public const string PushLastRegistration = "lastPushRegistration";
|
||||
public const string PasswordGeneratorLength = "pwGenerator:length";
|
||||
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()
|
||||
{
|
||||
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 webCell = new ToolsViewCell("bitwarden Web Vault", "Manage your logins from any web browser with the bitwarden web vault.", "globe");
|
||||
webCell.Tapped += WebCell_Tapped;
|
||||
|
@ -57,6 +58,11 @@ namespace Bit.App.Pages
|
|||
Content = table;
|
||||
}
|
||||
|
||||
private void GeneratorCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
Navigation.PushModalAsync(new ExtendedNavigationPage(new ToolsPasswordGeneratorPage()));
|
||||
}
|
||||
|
||||
private void WebCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var pushPromptShow = _settings.GetValueOrDefault<bool>(Constants.PushPromptShown);
|
||||
var pushPromptShow = _settings.GetValueOrDefault<bool>(Constants.PushInitialPromptShown);
|
||||
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.
|
||||
For the best possible experience, please select ""Ok"" on the following prompt when asked to enable push notifications.",
|
||||
"Enable Automatic Syncing", "Ok, got it!");
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
_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)
|
||||
{
|
||||
Debug.WriteLine("Registered device with server.");
|
||||
_settings.AddOrUpdateValue(Constants.PushLastRegistration, DateTime.UtcNow);
|
||||
_settings.AddOrUpdateValue(Constants.PushLastRegistrationDate, DateTime.UtcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -87,7 +87,7 @@ namespace Bit.App.Services
|
|||
public void OnUnregistered(DeviceType deviceType)
|
||||
{
|
||||
Debug.WriteLine("Push Notification - Device Unnregistered");
|
||||
_settings.Remove(Constants.PushLastRegistration);
|
||||
_settings.Remove(Constants.PushLastRegistrationDate);
|
||||
}
|
||||
|
||||
public void OnError(string message, DeviceType deviceType)
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace Bit.iOS.Extension
|
|||
.RegisterType<IFolderService, FolderService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ISiteService, SiteService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ISyncService, SyncService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
|
||||
//.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
||||
// Repositories
|
||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||
|
@ -101,10 +102,10 @@ namespace Bit.iOS.Extension
|
|||
.RegisterType<ISiteRepository, SiteRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ISiteApiRepository, SiteApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager());
|
||||
// Other
|
||||
//.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
||||
//.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
|
||||
//.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
|
||||
// Other
|
||||
//.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
||||
//.RegisterInstance(UserDialogs.Instance, new ContainerControlledLifetimeManager())
|
||||
//.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
|
||||
|
||||
ISettings settings = new Settings("group.com.8bit.bitwarden");
|
||||
container.RegisterInstance(settings, new ContainerControlledLifetimeManager());
|
||||
|
|
|
@ -185,6 +185,7 @@ namespace Bit.iOS
|
|||
.RegisterType<IClipboardService, ClipboardService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IPushNotificationListener, PushNotificationListener>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAppIdService, AppIdService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IPasswordGenerationService, PasswordGenerationService>(new ContainerControlledLifetimeManager())
|
||||
// Repositories
|
||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||
|
|
|
@ -42,6 +42,14 @@
|
|||
<HintPath>..\..\packages\NSubstitute.1.10.0.0\lib\net45\NSubstitute.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</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="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>
|
||||
|
@ -69,6 +77,7 @@
|
|||
<Otherwise />
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="PasswordGeneratorServiceTests.cs" />
|
||||
<Compile Include="CryptoServiceTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</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"?>
|
||||
<packages>
|
||||
<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.abstractions" version="2.0.0" targetFramework="net46" />
|
||||
<package id="xunit.assert" version="2.1.0" targetFramework="net46" />
|
||||
|
|
Loading…
Reference in a new issue