diff --git a/src/App/Abstractions/Services/IPasswordGenerationService.cs b/src/App/Abstractions/Services/IPasswordGenerationService.cs index 14e882f8b..66a8d4902 100644 --- a/src/App/Abstractions/Services/IPasswordGenerationService.cs +++ b/src/App/Abstractions/Services/IPasswordGenerationService.cs @@ -2,6 +2,16 @@ { public interface IPasswordGenerationService { - string GeneratePassword(); + string GeneratePassword( + int? length = null, + bool? uppercase = null, + bool? lowercase = null, + bool? numbers = null, + bool? special = null, + bool? ambiguous = null, + int? minUppercase = null, + int? minLowercase = null, + int? minNumbers = null, + int? minSpecial = null); } } diff --git a/src/App/Services/PasswordGenerationService.cs b/src/App/Services/PasswordGenerationService.cs index 94927f381..667f13ad0 100644 --- a/src/App/Services/PasswordGenerationService.cs +++ b/src/App/Services/PasswordGenerationService.cs @@ -17,78 +17,88 @@ namespace Bit.App.Services _settings = settings; } - public string GeneratePassword() + public string GeneratePassword( + int? length = null, + bool? uppercase = null, + bool? lowercase = null, + bool? numbers = null, + bool? special = null, + bool? ambiguous = null, + int? minUppercase = null, + int? minLowercase = null, + int? minNumbers = null, + int? minSpecial = null) { - int minUppercase = 1, - minLowercase = 1, - minNumbers = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, 1), - minSpecial = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, 1), - length = _settings.GetValueOrDefault(Constants.PasswordGeneratorLength, 10); + int minUppercaseValue = minUppercase.GetValueOrDefault(1), + minLowercaseValue = minLowercase.GetValueOrDefault(1), + minNumbersValue = minNumbers.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, 1)), + minSpecialValue = minSpecial.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, 1)), + lengthValue = length.GetValueOrDefault(_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); + bool uppercaseValue = uppercase.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, true)), + lowercaseValue = lowercase.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, true)), + numbersValue = numbers.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, true)), + specialValue = special.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, true)), + ambiguousValue = ambiguous.GetValueOrDefault(_settings.GetValueOrDefault(Constants.PasswordGeneratorAmbiguous, false)); // Sanitize - if(uppercase && minUppercase < 0) + if(uppercaseValue && minUppercaseValue < 0) { - minUppercase = 1; + minUppercaseValue = 1; } - if(lowercase && minLowercase < 0) + if(lowercaseValue && minLowercaseValue < 0) { - minLowercase = 1; + minLowercaseValue = 1; } - if(numbers && minNumbers < 0) + if(numbersValue && minNumbersValue < 0) { - minNumbers = 1; + minNumbersValue = 1; } - if(special && minSpecial < 0) + if(specialValue && minSpecialValue < 0) { - minSpecial = 1; + minSpecialValue = 1; } - if(length < 1) + if(lengthValue < 1) { - length = 10; + lengthValue = 10; } - var minLength = minUppercase + minLowercase + minNumbers + minSpecial; - if(length < minLength) + var minLength = minUppercaseValue + minLowercaseValue + minNumbersValue + minSpecialValue; + if(lengthValue < minLength) { - length = minLength; + lengthValue = minLength; } var positionsBuilder = new StringBuilder(); - if(lowercase && minLowercase > 0) + if(lowercaseValue && minLowercaseValue > 0) { - for(int i = 0; i < minLowercase; i++) + for(int i = 0; i < minLowercaseValue; i++) { positionsBuilder.Append("l"); } } - if(uppercase && minUppercase > 0) + if(uppercaseValue && minUppercaseValue > 0) { - for(int i = 0; i < minUppercase; i++) + for(int i = 0; i < minUppercaseValue; i++) { positionsBuilder.Append("u"); } } - if(numbers && minNumbers > 0) + if(numbersValue && minNumbersValue > 0) { - for(int i = 0; i < minNumbers; i++) + for(int i = 0; i < minNumbersValue; i++) { positionsBuilder.Append("n"); } } - if(special && minSpecial > 0) + if(specialValue && minSpecialValue > 0) { - for(int i = 0; i < minSpecial; i++) + for(int i = 0; i < minSpecialValue; i++) { positionsBuilder.Append("s"); } } - while(positionsBuilder.Length < length) + while(positionsBuilder.Length < lengthValue) { positionsBuilder.Append("a"); } @@ -100,43 +110,43 @@ namespace Bit.App.Services var allCharSet = string.Empty; var lowercaseCharSet = "abcdefghijkmnopqrstuvwxyz"; - if(ambiguous) + if(ambiguousValue) { lowercaseCharSet = string.Concat(lowercaseCharSet, "l"); } - if(lowercase) + if(lowercaseValue) { allCharSet = string.Concat(allCharSet, lowercaseCharSet); } var uppercaseCharSet = "ABCDEFGHIJKLMNPQRSTUVWXYZ"; - if(ambiguous) + if(ambiguousValue) { uppercaseCharSet = string.Concat(uppercaseCharSet, "O"); } - if(uppercase) + if(uppercaseValue) { allCharSet = string.Concat(allCharSet, uppercaseCharSet); } var numberCharSet = "23456789"; - if(ambiguous) + if(ambiguousValue) { numberCharSet = string.Concat(numberCharSet, "01"); } - if(numbers) + if(numbersValue) { allCharSet = string.Concat(allCharSet, numberCharSet); } var specialCharSet = "!@#$%^&*"; - if(special) + if(specialValue) { allCharSet = string.Concat(allCharSet, specialCharSet); } var password = new StringBuilder(); - for(var i = 0; i < length; i++) + for(var i = 0; i < lengthValue; i++) { string positionChars = string.Empty; switch(positions[i]) diff --git a/src/iOS.Core/Utilities/Dialogs.cs b/src/iOS.Core/Utilities/Dialogs.cs index 9065d0eac..a5cffccb0 100644 --- a/src/iOS.Core/Utilities/Dialogs.cs +++ b/src/iOS.Core/Utilities/Dialogs.cs @@ -20,6 +20,13 @@ namespace Bit.iOS.Core.Utilities return alert; } + public static UIAlertController CreateMessageAlert(string message) + { + var alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert); + alert.View.TintColor = UIColor.Black; + return alert; + } + public static UIAlertController CreateAlert(string title, string message, string accept) { var alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert); diff --git a/src/iOS.Core/Views/SliderTableViewCell.cs b/src/iOS.Core/Views/SliderTableViewCell.cs index 0c57671e4..54c763848 100644 --- a/src/iOS.Core/Views/SliderTableViewCell.cs +++ b/src/iOS.Core/Views/SliderTableViewCell.cs @@ -6,33 +6,51 @@ namespace Bit.iOS.Core.Views public class SliderTableViewCell : UITableViewCell { private string _detailRightSpace = "\t"; + private int _value; - public SliderTableViewCell(string labelName, float value, float min, float max) + public SliderTableViewCell(string labelName, int value, int min, int max) : base(UITableViewCellStyle.Value1, nameof(SwitchTableViewCell)) { TextLabel.Text = labelName; - DetailTextLabel.Text = string.Concat(value.ToString(), _detailRightSpace); DetailTextLabel.TextColor = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f); Slider = new UISlider { MinValue = min, MaxValue = max, - Value = value, TintColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f), Frame = new CoreGraphics.CGRect(0, 0, 180, 20) }; Slider.ValueChanged += Slider_ValueChanged; + Value = value; AccessoryView = Slider; } private void Slider_ValueChanged(object sender, EventArgs e) { - Slider.Value = Convert.ToInt32(Math.Round(Slider.Value, 0)); - DetailTextLabel.Text = string.Concat(Slider.Value.ToString(), _detailRightSpace); + var newValue = Convert.ToInt32(Math.Round(Slider.Value, 0)); + bool valueChanged = newValue != Value; + + Value = newValue; + + if(valueChanged) + { + ValueChanged(this, null); + } } public UISlider Slider { get; set; } + public int Value + { + get { return _value; } + set + { + _value = value; + Slider.Value = value; + DetailTextLabel.Text = string.Concat(value.ToString(), _detailRightSpace); + } + } + public event EventHandler ValueChanged; } } diff --git a/src/iOS.Core/Views/StepperTableViewCell.cs b/src/iOS.Core/Views/StepperTableViewCell.cs index 8b8400847..61846f15c 100644 --- a/src/iOS.Core/Views/StepperTableViewCell.cs +++ b/src/iOS.Core/Views/StepperTableViewCell.cs @@ -9,31 +9,43 @@ namespace Bit.iOS.Core.Views // This is a bit of a hack, but I did not see a way to specify a margin on the // detaul DetailTextLabel or AccessoryView private string _detailRightSpace = "\t"; + private int _value; - public StepperTableViewCell(string labelName, double value, double min, double max, double increment) + public StepperTableViewCell(string labelName, int value, int min, int max, int increment) : base(UITableViewCellStyle.Value1, nameof(SwitchTableViewCell)) { TextLabel.Text = labelName; - DetailTextLabel.Text = string.Concat(value.ToString(), _detailRightSpace); DetailTextLabel.TextColor = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f); Stepper = new UIStepper { TintColor = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f), - Value = value, MinimumValue = min, MaximumValue = max }; Stepper.ValueChanged += Stepper_ValueChanged; + Value = value; AccessoryView = Stepper; } private void Stepper_ValueChanged(object sender, EventArgs e) { - DetailTextLabel.Text = string.Concat(Stepper.Value.ToString(), _detailRightSpace); + Value = Convert.ToInt32(Stepper.Value); + ValueChanged(this, null); } public UIStepper Stepper { get; private set; } + public int Value + { + get { return _value; } + set + { + _value = value; + Stepper.Value = value; + DetailTextLabel.Text = string.Concat(value.ToString(), _detailRightSpace); + } + } + public event EventHandler ValueChanged; } } diff --git a/src/iOS.Core/Views/SwitchTableViewCell.cs b/src/iOS.Core/Views/SwitchTableViewCell.cs index 5b6b7b2e7..bb8dab2f2 100644 --- a/src/iOS.Core/Views/SwitchTableViewCell.cs +++ b/src/iOS.Core/Views/SwitchTableViewCell.cs @@ -10,8 +10,16 @@ namespace Bit.iOS.Core.Views { TextLabel.Text = labelName; AccessoryView = Switch; + + Switch.ValueChanged += Switch_ValueChanged; + } + + private void Switch_ValueChanged(object sender, EventArgs e) + { + ValueChanged(this, null); } public UISwitch Switch { get; set; } = new UISwitch(); + public event EventHandler ValueChanged; } } diff --git a/src/iOS.Extension/LoadingViewController.cs b/src/iOS.Extension/LoadingViewController.cs index b1b86714a..4d2c09add 100644 --- a/src/iOS.Extension/LoadingViewController.cs +++ b/src/iOS.Extension/LoadingViewController.cs @@ -98,7 +98,6 @@ namespace Bit.iOS.Extension .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) - //.RegisterType(new ContainerControlledLifetimeManager()) // Repositories .RegisterType(new ContainerControlledLifetimeManager()) .RegisterType(new ContainerControlledLifetimeManager()) diff --git a/src/iOS.Extension/PasswordGeneratorViewController.cs b/src/iOS.Extension/PasswordGeneratorViewController.cs index c1f21f121..995c4417d 100644 --- a/src/iOS.Extension/PasswordGeneratorViewController.cs +++ b/src/iOS.Extension/PasswordGeneratorViewController.cs @@ -1,20 +1,15 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using Bit.App.Abstractions; -using Bit.App.Models; -using Bit.App.Resources; using Bit.iOS.Core.Views; using Bit.iOS.Extension.Models; using Foundation; using UIKit; using XLabs.Ioc; -using Bit.App; -using Plugin.Connectivity.Abstractions; -using Bit.iOS.Core.Utilities; using Plugin.Settings.Abstractions; using CoreGraphics; +using Bit.App; +using Bit.iOS.Core.Utilities; namespace Bit.iOS.Extension { @@ -51,7 +46,6 @@ namespace Bit.iOS.Extension View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); - PasswordLabel.Text = _passwordGenerationService.GeneratePassword(); var descriptor = UIFontDescriptor.PreferredBody; PasswordLabel.Font = UIFont.FromName("Courier", descriptor.PointSize * 1.3f); PasswordLabel.LineBreakMode = UILineBreakMode.TailTruncation; @@ -73,9 +67,94 @@ namespace Bit.iOS.Extension OptionsTableViewController.View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f); } + UppercaseCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorUppercase, true); + LowercaseCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorLowercase, true); + SpecialCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorSpecial, true); + NumbersCell.Switch.On = _settings.GetValueOrDefault(Constants.PasswordGeneratorNumbers, true); + MinNumbersCell.Value = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinNumbers, 1); + MinSpecialCell.Value = _settings.GetValueOrDefault(Constants.PasswordGeneratorMinSpecial, 1); + LengthCell.Value = _settings.GetValueOrDefault(Constants.PasswordGeneratorLength, 10); + + UppercaseCell.ValueChanged += Options_ValueChanged; + LowercaseCell.ValueChanged += Options_ValueChanged; + NumbersCell.ValueChanged += Options_ValueChanged; + SpecialCell.ValueChanged += Options_ValueChanged; + MinNumbersCell.ValueChanged += Options_ValueChanged; + MinSpecialCell.ValueChanged += Options_ValueChanged; + LengthCell.ValueChanged += Options_ValueChanged; + + // Adjust based on context password options + if(Context.PasswordOptions != null) + { + if(Context.PasswordOptions.RequireDigits) + { + NumbersCell.Switch.On = true; + NumbersCell.Switch.Enabled = false; + + if(MinNumbersCell.Value < 1) + { + MinNumbersCell.Value = 1; + } + + MinNumbersCell.Stepper.MinimumValue = 1; + } + + if(Context.PasswordOptions.RequireSymbols) + { + SpecialCell.Switch.On = true; + SpecialCell.Switch.Enabled = false; + + if(MinSpecialCell.Value < 1) + { + MinSpecialCell.Value = 1; + } + + MinSpecialCell.Stepper.MinimumValue = 1; + } + + if(Context.PasswordOptions.MinLength < Context.PasswordOptions.MaxLength) + { + if(Context.PasswordOptions.MinLength > 0 && Context.PasswordOptions.MinLength > LengthCell.Slider.MinValue) + { + if(LengthCell.Value < Context.PasswordOptions.MinLength) + { + LengthCell.Slider.Value = Context.PasswordOptions.MinLength; + } + + LengthCell.Slider.MinValue = Context.PasswordOptions.MinLength; + } + + if(Context.PasswordOptions.MaxLength > 5 && Context.PasswordOptions.MaxLength < LengthCell.Slider.MaxValue) + { + if(LengthCell.Value > Context.PasswordOptions.MaxLength) + { + LengthCell.Slider.Value = Context.PasswordOptions.MaxLength; + } + + LengthCell.Slider.MaxValue = Context.PasswordOptions.MaxLength; + } + } + } + + GeneratePassword(); base.ViewDidLoad(); } + private void Options_ValueChanged(object sender, EventArgs e) + { + if(InvalidState()) + { + LowercaseCell.Switch.On = true; + } + + GeneratePassword(); + } + + private bool InvalidState() + { + return !LowercaseCell.Switch.On && !UppercaseCell.Switch.On && !NumbersCell.Switch.On && !SpecialCell.Switch.On; + } + partial void SelectBarButton_Activated(UIBarButtonItem sender) { throw new NotImplementedException(); @@ -86,6 +165,18 @@ namespace Bit.iOS.Extension throw new NotImplementedException(); } + private void GeneratePassword() + { + PasswordLabel.Text = _passwordGenerationService.GeneratePassword( + length: LengthCell.Value, + uppercase: UppercaseCell.Switch.On, + lowercase: LowercaseCell.Switch.On, + numbers: NumbersCell.Switch.On, + special: SpecialCell.Switch.On, + minSpecial: MinSpecialCell.Value, + minNumbers: MinNumbersCell.Value); + } + public class TableSource : UITableViewSource { private PasswordGeneratorViewController _controller; @@ -197,23 +288,47 @@ namespace Bit.iOS.Extension return null; } + public override string TitleForFooter(UITableView tableView, nint section) + { + if(section == 1) + { + return "Option defaults are set from the main bitwarden app's password generator tool."; + } + + return null; + } + public override void RowSelected(UITableView tableView, NSIndexPath indexPath) { if(indexPath.Section == 0) { if(indexPath.Row == 0) { - _controller.PasswordLabel.Text = _controller._passwordGenerationService.GeneratePassword(); + _controller.GeneratePassword(); } else if(indexPath.Row == 1) { - // TODO: copy to clipboard + UIPasteboard clipboard = UIPasteboard.General; + clipboard.String = _controller.PasswordLabel.Text; + var alert = Dialogs.CreateMessageAlert("Copied!"); + _controller.PresentViewController(alert, true, () => + { + _controller.DismissViewController(true, null); + }); } } tableView.DeselectRow(indexPath, true); tableView.EndEditing(true); } + + public NSDate DateTimeToNSDate(DateTime date) + { + DateTime reference = TimeZone.CurrentTimeZone.ToLocalTime( + new DateTime(2001, 1, 1, 0, 0, 0)); + return NSDate.FromTimeIntervalSinceReferenceDate( + (date - reference).TotalSeconds); + } } } }