Colorized passwords (#424)

* Added utility to format passwords using spans

* Use the password formatter to render the cipher password

* Colorize the password in the password generator
This commit is contained in:
Andreas Schneider 2018-12-10 15:30:11 +01:00 committed by Kyle Spearrin
parent 8fc5ad099b
commit 421f7e8799
5 changed files with 98 additions and 7 deletions

View file

@ -1,5 +1,7 @@
using System;
using System.ComponentModel;
using Bit.App.Utilities;
using Xamarin.Forms;
namespace Bit.App.Models.Page
{
@ -19,9 +21,15 @@ namespace Bit.App.Models.Page
{
_password = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Password)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(FormattedPassword)));
}
}
public FormattedString FormattedPassword
{
get { return PasswordFormatter.FormatPassword(_password); }
}
public string Length
{
get { return _length; }

View file

@ -4,6 +4,7 @@ using Xamarin.Forms;
using System.Collections.Generic;
using Bit.App.Enums;
using Bit.App.Resources;
using Bit.App.Utilities;
namespace Bit.App.Models.Page
{
@ -122,7 +123,7 @@ namespace Bit.App.Models.Page
{
_loginPassword = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedLoginPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(FormattedLoginPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginPassword)));
}
}
@ -134,12 +135,12 @@ namespace Bit.App.Models.Page
{
_loginRevealPassword = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(RevealLoginPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedLoginPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(FormattedLoginPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginShowHideImage)));
}
}
public string MaskedLoginPassword => RevealLoginPassword ?
LoginPassword : LoginPassword == null ? null : MaskedPasswordString;
public FormattedString FormattedLoginPassword => RevealLoginPassword ?
PasswordFormatter.FormatPassword(LoginPassword) : LoginPassword == null ? null : MaskedPasswordString;
public ImageSource LoginShowHideImage => RevealLoginPassword ?
ImageSource.FromFile("eye_slash.png") : ImageSource.FromFile("eye.png");

View file

@ -67,7 +67,7 @@ namespace Bit.App.Pages
Tgr = new TapGestureRecognizer();
Password.GestureRecognizers.Add(Tgr);
Password.SetBinding(Label.TextProperty, nameof(PasswordGeneratorPageModel.Password));
Password.SetBinding(Label.FormattedTextProperty, nameof(PasswordGeneratorPageModel.FormattedPassword));
SliderCell = new SliderViewCell(this, _passwordGenerationService, _settings);

View file

@ -129,8 +129,8 @@ namespace Bit.App.Pages
// Password
LoginPasswordCell = new LabeledValueCell(AppResources.Password, button1Image: string.Empty,
button2Image: "clipboard.png");
LoginPasswordCell.Value.SetBinding(Label.TextProperty,
nameof(VaultViewCipherPageModel.MaskedLoginPassword));
LoginPasswordCell.Value.SetBinding(Label.FormattedTextProperty,
nameof(VaultViewCipherPageModel.FormattedLoginPassword));
LoginPasswordCell.Button1.SetBinding(Button.ImageProperty,
nameof(VaultViewCipherPageModel.LoginShowHideImage));
LoginPasswordCell.Button1.Command =

View file

@ -0,0 +1,82 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Utilities
{
/**
* Helper class to format a password with numeric encoding to separate
* normal text from numbers and special characters.
*/
class PasswordFormatter
{
/**
* This enum is used for the statemachine when building the colorized
* password string.
*/
private enum CharType
{
None,
Normal,
Number,
Special
}
public static FormattedString FormatPassword(String password)
{
var result = new FormattedString();
// Start off with an empty span to prevent possible NPEs. Due to the way the statemachine
// works, this will actually always be replaced by a new span anyway.
var currentSpan = new Span();
// Start with an otherwise uncovered case so we will definitely enter the "something changed"
// state.
var currentType = CharType.None;
foreach (var c in password)
{
// First, identify what the current char is.
CharType charType;
if (char.IsLetter(c))
{
charType = CharType.Normal;
}
else if (char.IsDigit(c))
{
charType = CharType.Number;
}
else
{
charType = CharType.Special;
}
// If the char type changed, build a new span to append the text to.
if (charType != currentType)
{
currentSpan = new Span();
result.Spans.Add(currentSpan);
currentType = charType;
// Switch the color if it is not a normal text. Otherwise leave the
// default value.
switch (currentType)
{
case CharType.Number:
{
currentSpan.TextColor = Color.DodgerBlue;
break;
}
case CharType.Special:
{
currentSpan.TextColor = Color.Firebrick;
break;
}
}
}
currentSpan.Text += c;
}
return result;
}
}
}