From 5c20d7c2e0c664fa857dc06829ee52973891b1f1 Mon Sep 17 00:00:00 2001 From: erri120 <erri120@protonmail.com> Date: Tue, 5 May 2020 16:17:55 +0200 Subject: [PATCH] Added MFA support for INeedsLoginCredentials --- Wabbajack.Lib/Downloaders/INeedsLogin.cs | 17 ++++++++--- Wabbajack.Lib/Downloaders/MEGADownloader.cs | 30 +++++++++++-------- Wabbajack.Lib/Wabbajack.Lib.csproj | 2 +- .../Settings/CredentialsLoginVM.cs | 16 ++++++++-- .../Views/Settings/CredentialsLoginView.xaml | 14 +++++---- .../Settings/CredentialsLoginView.xaml.cs | 6 ++++ 6 files changed, 59 insertions(+), 26 deletions(-) diff --git a/Wabbajack.Lib/Downloaders/INeedsLogin.cs b/Wabbajack.Lib/Downloaders/INeedsLogin.cs index 8ce03afe..b9aae38e 100644 --- a/Wabbajack.Lib/Downloaders/INeedsLogin.cs +++ b/Wabbajack.Lib/Downloaders/INeedsLogin.cs @@ -16,20 +16,29 @@ namespace Wabbajack.Lib.Downloaders Uri? IconUri { get; } } + public enum LoginReturnCode + { + InternalError = -1, + Success = 0, + BadInput = 1, + BadCredentials = 2, + NeedsMFA = 3, + } + public struct LoginReturnMessage { public string Message; - public bool Failure; + public LoginReturnCode ReturnCode; - public LoginReturnMessage(string message, bool failure) + public LoginReturnMessage(string message, LoginReturnCode returnCode) { Message = message; - Failure = failure; + ReturnCode = returnCode; } } public interface INeedsLoginCredentials : INeedsLogin { - LoginReturnMessage LoginWithCredentials(string username, SecureString password); + LoginReturnMessage LoginWithCredentials(string username, SecureString password, string? mfa); } } diff --git a/Wabbajack.Lib/Downloaders/MEGADownloader.cs b/Wabbajack.Lib/Downloaders/MEGADownloader.cs index cac854fe..83f172b9 100644 --- a/Wabbajack.Lib/Downloaders/MEGADownloader.cs +++ b/Wabbajack.Lib/Downloaders/MEGADownloader.cs @@ -38,34 +38,34 @@ namespace Wabbajack.Lib.Downloaders { Email = infos.Email, Hash = infos.Hash, - PasswordAesKey = infos.PasswordAesKey + PasswordAesKey = infos.PasswordAesKey, + MFAKey = infos.MFAKey }; } public MegaApiClient.AuthInfos ToAuthInfos() { - //TODO: MFAKey in the update - return new MegaApiClient.AuthInfos(Email, Hash, PasswordAesKey); + return new MegaApiClient.AuthInfos(Email, Hash, PasswordAesKey, MFAKey); } } - public LoginReturnMessage LoginWithCredentials(string username, SecureString password) + public LoginReturnMessage LoginWithCredentials(string username, SecureString password, string? mfa) { MegaApiClient.AuthInfos authInfos; try { - authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString()); + authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString(), mfa); } catch (ApiException e) { return e.ApiResultCode switch { ApiResultCode.BadArguments => new LoginReturnMessage($"Email or password was wrong! {e.Message}", - true), + LoginReturnCode.BadCredentials), ApiResultCode.InternalError => new LoginReturnMessage( - $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", true), - _ => new LoginReturnMessage($"Error generating authentication information! {e.Message}", true) + $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", LoginReturnCode.InternalError), + _ => new LoginReturnMessage($"Error generating authentication information! {e.Message}", LoginReturnCode.InternalError) }; } @@ -78,10 +78,14 @@ namespace Wabbajack.Lib.Downloaders return e.ApiResultCode switch { ApiResultCode.TwoFactorAuthenticationError => new LoginReturnMessage( - $"Two-Factor Authentication needs to be disabled before login! {e.Message}", true), + $"Two-Factor Authentication is enabled. Input your TFA key above and try again! {e.Message}", LoginReturnCode.NeedsMFA), ApiResultCode.InternalError => new LoginReturnMessage( - $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", true), - _ => new LoginReturnMessage($"Error during login: {e.Message}", true) + $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", LoginReturnCode.InternalError), + ApiResultCode.RequestIncomplete => new LoginReturnMessage( + $"Bad credentials! {e.Message}", LoginReturnCode.BadCredentials), + ApiResultCode.ResourceNotExists => new LoginReturnMessage( + $"Bad credentials! {e.Message}", LoginReturnCode.BadCredentials), + _ => new LoginReturnMessage($"Error during login: {e.Message}", LoginReturnCode.InternalError) }; } @@ -92,7 +96,7 @@ namespace Wabbajack.Lib.Downloaders } return new LoginReturnMessage("Logged in successfully, you can now close this window.", - !MegaApiClient.IsLoggedIn || !Utils.HaveEncryptedJson(DataName)); + !MegaApiClient.IsLoggedIn || !Utils.HaveEncryptedJson(DataName) ? LoginReturnCode.Success : LoginReturnCode.InternalError); } public MegaDownloader() @@ -133,7 +137,7 @@ namespace Wabbajack.Lib.Downloaders private static MegaApiClient MegaApiClient => DownloadDispatcher.GetInstance<MegaDownloader>().MegaApiClient; - private static AsyncLock _loginLock = new AsyncLock(); + private static readonly AsyncLock _loginLock = new AsyncLock(); private static async Task MegaLogin() { using var _ = await _loginLock.WaitAsync(); diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj index f13f5686..da89051e 100644 --- a/Wabbajack.Lib/Wabbajack.Lib.csproj +++ b/Wabbajack.Lib/Wabbajack.Lib.csproj @@ -26,7 +26,7 @@ <Version>1.11.23</Version> </PackageReference> <PackageReference Include="MegaApiClient"> - <Version>1.8.0</Version> + <Version>1.8.1</Version> </PackageReference> <PackageReference Include="Microsoft.CSharp"> <Version>4.7.0</Version> diff --git a/Wabbajack/View Models/Settings/CredentialsLoginVM.cs b/Wabbajack/View Models/Settings/CredentialsLoginVM.cs index d0d7a195..38715a39 100644 --- a/Wabbajack/View Models/Settings/CredentialsLoginVM.cs +++ b/Wabbajack/View Models/Settings/CredentialsLoginVM.cs @@ -15,12 +15,18 @@ namespace Wabbajack [Reactive] public string Username { get; set; } + [Reactive] + public string MFAKey { get; set; } + [Reactive] public LoginReturnMessage ReturnMessage { get; set; } private readonly ObservableAsPropertyHelper<bool> _loginEnabled; public bool LoginEnabled => _loginEnabled.Value; + private readonly ObservableAsPropertyHelper<bool> _mfaVisible; + public bool MFAVisible => _mfaVisible.Value; + private readonly INeedsLoginCredentials _downloader; public CredentialsLoginVM(INeedsLoginCredentials downloader) @@ -31,6 +37,10 @@ namespace Wabbajack .Select(IsValidAddress) .ToGuiProperty(this, nameof(LoginEnabled)); + + _mfaVisible = this.WhenAny(x => x.ReturnMessage) + .Select(x => x.ReturnCode == LoginReturnCode.NeedsMFA) + .ToGuiProperty(this, nameof(MFAVisible)); } public void Login(SecureString password) @@ -39,17 +49,17 @@ namespace Wabbajack { if (password == null || password.Length == 0) { - ReturnMessage = new LoginReturnMessage("You need to input a password!", true); + ReturnMessage = new LoginReturnMessage("You need to input a password!", LoginReturnCode.BadInput); return; } - ReturnMessage = _downloader.LoginWithCredentials(Username, password); + ReturnMessage = _downloader.LoginWithCredentials(Username, password, string.IsNullOrWhiteSpace(MFAKey) ? null : MFAKey); password.Clear(); } catch (Exception e) { Utils.Error(e, "Exception while trying to login"); - ReturnMessage = new LoginReturnMessage($"Unhandled exception: {e.Message}", true); + ReturnMessage = new LoginReturnMessage($"Unhandled exception: {e.Message}", LoginReturnCode.InternalError); } } diff --git a/Wabbajack/Views/Settings/CredentialsLoginView.xaml b/Wabbajack/Views/Settings/CredentialsLoginView.xaml index 528e56df..cde4c645 100644 --- a/Wabbajack/Views/Settings/CredentialsLoginView.xaml +++ b/Wabbajack/Views/Settings/CredentialsLoginView.xaml @@ -11,10 +11,11 @@ d:DesignHeight="450" d:DesignWidth="800"> <Grid Margin="16"> <Grid.RowDefinitions> - <RowDefinition Height="60"/> + <RowDefinition Height="40"/> <RowDefinition Height="60"/> <RowDefinition Height="40"/> - <RowDefinition Height="auto"/> + <RowDefinition Height="40"/> + <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> @@ -27,12 +28,15 @@ <TextBox FontSize="20" x:Name="Username" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"/> <TextBlock FontSize="20" Grid.Row="1" Grid.Column="0" Margin="0 16 8 0">Password:</TextBlock> - <PasswordBox Margin="0 16 0 -8" FontSize="20" x:Name="Password" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/> + <PasswordBox Margin="0 16 0 8" FontSize="20" x:Name="Password" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/> - <Button Margin="8 8 0 0" x:Name="LoginButton" Grid.Row="2" Grid.Column="3" Click="LoginButton_OnClick"> + <TextBlock FontSize="20" Grid.Row="2" Grid.Column="0" Margin="0 16 8 0" x:Name="MFAText">MFA Key:</TextBlock> + <TextBox FontSize="20" x:Name="MFA" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"/> + + <Button Margin="8 8 0 0" x:Name="LoginButton" Grid.Row="3" Grid.Column="3" Click="LoginButton_OnClick"> <TextBlock FontSize="14">Login</TextBlock> </Button> - <TextBlock x:Name="Message" FontSize="20" Margin="0 16 0 0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"/> + <TextBlock x:Name="Message" FontSize="20" Margin="0 16 0 0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" TextWrapping="Wrap"/> </Grid> </rxui:ReactiveUserControl> diff --git a/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs b/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs index efc1951c..f369c2bf 100644 --- a/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs +++ b/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs @@ -24,6 +24,12 @@ namespace Wabbajack .DisposeWith(disposable); this.Bind(ViewModel, x => x.LoginEnabled, x => x.LoginButton.IsEnabled) .DisposeWith(disposable); + this.Bind(ViewModel, x => x.MFAKey, x => x.MFA.Text) + .DisposeWith(disposable); + this.OneWayBind(ViewModel, x => x.MFAVisible, x => x.MFA.Visibility) + .DisposeWith(disposable); + this.OneWayBind(ViewModel, x => x.MFAVisible, x => x.MFAText.Visibility) + .DisposeWith(disposable); this.OneWayBind(ViewModel, x => x.ReturnMessage.Message, x => x.Message.Text) .DisposeWith(disposable); });