From d82c0d7d71a68b3f0f5c5f940d2760694a1453f6 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 18 Jul 2016 19:16:27 -0400 Subject: [PATCH] Lock by master password page. --- src/App/Abstractions/Services/IAuthService.cs | 1 + src/App/App.cs | 5 +- src/App/App.csproj | 1 + src/App/Pages/Lock/LockPasswordPage.cs | 116 ++++++++++++++++++ src/App/Pages/LoginPage.cs | 1 + src/App/Pages/Vault/VaultListSitesPage.cs | 3 +- src/App/Services/AuthService.cs | 30 +++++ 7 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 src/App/Pages/Lock/LockPasswordPage.cs diff --git a/src/App/Abstractions/Services/IAuthService.cs b/src/App/Abstractions/Services/IAuthService.cs index 56246af7a..4e743c3ad 100644 --- a/src/App/Abstractions/Services/IAuthService.cs +++ b/src/App/Abstractions/Services/IAuthService.cs @@ -8,6 +8,7 @@ namespace Bit.App.Abstractions bool IsAuthenticated { get; } string Token { get; set; } string UserId { get; set; } + string Email { get; set; } string PIN { get; set; } void LogOut(); diff --git a/src/App/App.cs b/src/App/App.cs index 4f77aeefa..32b04d7df 100644 --- a/src/App/App.cs +++ b/src/App/App.cs @@ -216,7 +216,10 @@ namespace Bit.App } else { - // Use master password to unlock if no other methods are set + if((currentPage?.CurrentPage as LockPasswordPage) == null) + { + await Current.MainPage.Navigation.PushModalAsync(new ExtendedNavigationPage(new LockPasswordPage()), false); + } } } diff --git a/src/App/App.csproj b/src/App/App.csproj index d78acfb40..33c08fb2e 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -107,6 +107,7 @@ + diff --git a/src/App/Pages/Lock/LockPasswordPage.cs b/src/App/Pages/Lock/LockPasswordPage.cs new file mode 100644 index 000000000..ee572df70 --- /dev/null +++ b/src/App/Pages/Lock/LockPasswordPage.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading.Tasks; +using Acr.UserDialogs; +using Bit.App.Abstractions; +using Bit.App.Resources; +using Xamarin.Forms; +using XLabs.Ioc; +using Plugin.Settings.Abstractions; +using Bit.App.Models.Page; +using Bit.App.Controls; +using System.Linq; + +namespace Bit.App.Pages +{ + public class LockPasswordPage : ExtendedContentPage + { + private readonly IAuthService _authService; + private readonly IUserDialogs _userDialogs; + private readonly ISettings _settings; + private readonly ICryptoService _cryptoService; + + public LockPasswordPage() + : base(false) + { + _authService = Resolver.Resolve(); + _userDialogs = Resolver.Resolve(); + _settings = Resolver.Resolve(); + _cryptoService = Resolver.Resolve(); + + Init(); + } + + public Entry Password { get; private set; } + + public void Init() + { + Password = new ExtendedEntry + { + HasBorder = false, + IsPassword = true, + Placeholder = AppResources.MasterPassword, + ReturnType = Enums.ReturnType.Go + }; + + var logoutButton = new Button + { + Text = AppResources.LogOut, + Command = new Command(async () => await LogoutAsync()), + VerticalOptions = LayoutOptions.End, + Style = (Style)Application.Current.Resources["btn-primaryAccent"] + }; + + var stackLayout = new StackLayout + { + Padding = new Thickness(30, 40), + Spacing = 10, + Children = { Password, logoutButton } + }; + + var loginToolbarItem = new ToolbarItem("Submit", null, async () => + { + await CheckPasswordAsync(); + }, ToolbarItemOrder.Default, 0); + + ToolbarItems.Add(loginToolbarItem); + Title = "Verify Master Password"; + Content = stackLayout; + } + + protected override bool OnBackButtonPressed() + { + return false; + } + + protected override void OnAppearing() + { + base.OnAppearing(); + Password.Focus(); + } + + protected async Task CheckPasswordAsync() + { + if(string.IsNullOrWhiteSpace(Password.Text)) + { + await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), AppResources.Ok); + return; + } + + var key = _cryptoService.MakeKeyFromPassword(Password.Text, _authService.Email); + if(key.SequenceEqual(_cryptoService.Key)) + { + await Navigation.PopModalAsync(); + } + else + { + // TODO: keep track of invalid attempts and logout? + + _userDialogs.Alert("Invalid Master Password. Try again."); + Password.Text = string.Empty; + Password.Focus(); + } + } + + private async Task LogoutAsync() + { + if(!await _userDialogs.ConfirmAsync("Are you sure you want to log out?", null, AppResources.Yes, AppResources.Cancel)) + { + return; + } + + _authService.LogOut(); + await Navigation.PopModalAsync(); + Application.Current.MainPage = new HomePage(); + } + } +} diff --git a/src/App/Pages/LoginPage.cs b/src/App/Pages/LoginPage.cs index 41e0cb315..4419f8b67 100644 --- a/src/App/Pages/LoginPage.cs +++ b/src/App/Pages/LoginPage.cs @@ -129,6 +129,7 @@ namespace Bit.App.Pages _cryptoService.Key = key; _authService.Token = response.Result.Token; _authService.UserId = response.Result.Profile.Id; + _authService.Email = response.Result.Profile.Email; var syncTask = _syncService.FullSyncAsync(); Application.Current.MainPage = new MainPage(); diff --git a/src/App/Pages/Vault/VaultListSitesPage.cs b/src/App/Pages/Vault/VaultListSitesPage.cs index 3cf0401e5..5241b2a97 100644 --- a/src/App/Pages/Vault/VaultListSitesPage.cs +++ b/src/App/Pages/Vault/VaultListSitesPage.cs @@ -85,7 +85,8 @@ namespace Bit.App.Pages Search = new SearchBar { Placeholder = "Search vault", - FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Button)) + FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Button)), + CancelButtonColor = Color.FromHex("3c8dbc") }; Search.TextChanged += SearchBar_TextChanged; Search.SearchButtonPressed += SearchBar_SearchButtonPressed; diff --git a/src/App/Services/AuthService.cs b/src/App/Services/AuthService.cs index 6cd273f4d..f13eacd08 100644 --- a/src/App/Services/AuthService.cs +++ b/src/App/Services/AuthService.cs @@ -10,6 +10,7 @@ namespace Bit.App.Services public class AuthService : IAuthService { private const string TokenKey = "token"; + private const string EmailKey = "email"; private const string UserIdKey = "userId"; private const string PinKey = "pin"; @@ -19,6 +20,7 @@ namespace Bit.App.Services private readonly IAuthApiRepository _authApiRepository; private string _token; + private string _email; private string _userId; private string _pin; @@ -95,6 +97,33 @@ namespace Bit.App.Services } } + public string Email + { + get + { + if(_email != null) + { + return _email; + } + + _email = _settings.GetValueOrDefault(EmailKey); + return _email; + } + set + { + if(value != null) + { + _settings.AddOrUpdateValue(EmailKey, value); + } + else + { + _settings.Remove(EmailKey); + } + + _email = value; + } + } + public bool IsAuthenticated { get @@ -141,6 +170,7 @@ namespace Bit.App.Services { Token = null; UserId = null; + Email = null; _cryptoService.Key = null; }