using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.BL; using Tango.BL.Enumerations; using Tango.Core.Commands; using Tango.Core.Cryptography; using Tango.Core.DI; using Tango.MachineStudio.Common; using Tango.MachineStudio.Common.Authentication; using Tango.MachineStudio.Common.EventLogging; using Tango.MachineStudio.Common.Navigation; using Tango.MachineStudio.Common.Notifications; using Tango.MachineStudio.Common.Web; using Tango.MachineStudio.UI.Authentication; using Tango.MachineStudio.UI.Messages; using Tango.Settings; using Tango.SharedUI; using Tango.Web; using SimpleValidator.Extensions; using Tango.BL.Entities; using System.Data.Entity; using Tango.MachineStudio.Common.Buid; using System.Diagnostics; namespace Tango.MachineStudio.UI.ViewModels { /// /// Represents the Machine Studio login view, view model. /// /// public class LoginViewVM : ViewModel { private IAuthenticationProvider _authenticationProvider; private INavigationManager _navigationManager; private INotificationProvider _notificationProvider; private IEventLogger _eventLogger; private Rfc2898Cryptographer cryptographer; private MachineStudioSettings _settings; private MachineStudioWebClient _machineStudioWebClient; private TaskCompletionSource _updatePasswordCompletionSource; private IBuildProvider _buildProvider; private String _email; /// /// Gets or sets the email. /// [Required(ErrorMessage = "Email is required")] [EmailAddress(ErrorMessage = "Please enter a valid email")] public String Email { get { return _email; } set { _email = value; RaisePropertyChangedAuto(); } } private String _password; /// /// Gets or sets the password. /// [Required(ErrorMessage = "Password is required")] public String Password { get { return _password; } set { _password = value; RaisePropertyChangedAuto(); } } private String _serialNumber; /// /// Gets or sets the password. /// #if LITE [Required(ErrorMessage = "Serial Number is required")] #endif public String SerialNumber { get { return _serialNumber; } set { _serialNumber = value; RaisePropertyChangedAuto(); } } private DeploymentSlot _deploymentSlot; /// /// Gets or sets the deployment slot. /// public DeploymentSlot DeploymentSlot { get { return _deploymentSlot; } set { _deploymentSlot = value; RaisePropertyChangedAuto(); } } private bool _isLogging; /// /// Gets or sets a value indicating whether this instance is logging. /// public bool IsLogging { get { return _isLogging; } set { _isLogging = value; RaisePropertyChangedAuto(); } } private bool _showLogginDetails; public bool ShowLoggingDetails { get { return _showLogginDetails; } set { _showLogginDetails = value; RaisePropertyChangedAuto(); } } private bool _rememberMe; /// /// Gets or sets a value indicating whether to remember the last user email and password. /// public bool RememberMe { get { return _rememberMe; } set { _rememberMe = value; RaisePropertyChangedAuto(); } } private bool _enableSlotSelection; /// /// Gets or sets a value indicating whether to enable the deployment slot selection. /// public bool EnableSlotSelection { get { return _enableSlotSelection; } set { _enableSlotSelection = value; RaisePropertyChangedAuto(); } } private bool _isActiveDirectory; public bool IsActiveDirectory { get { return _isActiveDirectory; } set { _isActiveDirectory = value; RaisePropertyChangedAuto(); if (value) IsStandardUser = false; } } private bool _isStandardUser; public bool IsStandardUser { get { return _isStandardUser; } set { _isStandardUser = value; RaisePropertyChangedAuto(); if (value) IsActiveDirectory = false; } } private String _progressLog; public String ProgressLog { get { return _progressLog; } set { _progressLog = value; RaisePropertyChangedAuto(); } } private bool _isChangingPassword; public bool IsChangingPassword { get { return _isChangingPassword; } set { _isChangingPassword = value; RaisePropertyChangedAuto(); } } private String _newPassword1; public String NewPassword1 { get { return _newPassword1; } set { _newPassword1 = value; RaisePropertyChangedAuto(); } } private String _newPassword2; public String NewPassword2 { get { return _newPassword2; } set { _newPassword2 = value; RaisePropertyChangedAuto(); } } public List Environments { get; set; } /// /// Gets or sets the login command. /// public RelayCommand LoginCommand { get; set; } /// /// Gets or sets the update password command. /// public RelayCommand UpdatePasswordCommand { get; set; } /// /// Initializes a new instance of the class. /// /// The authentication provider. /// The navigation manager. /// The notification provider. public LoginViewVM(IBuildProvider buildProvider, MachineStudioWebClient machineStudioWebClient, IAuthenticationProvider authenticationProvider, INavigationManager navigationManager, INotificationProvider notificationProvider, IEventLogger eventLogger) { EnableSlotSelection = true; ShowLoggingDetails = true; _machineStudioWebClient = machineStudioWebClient; _buildProvider = buildProvider; _settings = SettingsManager.Default.GetOrCreate(); if (_buildProvider.BuildType == MSBuildType.Default) { Environments = Enum.GetValues(typeof(DeploymentSlot)).Cast().ToList(); } else { Environments = new List(); Environments.Add(DeploymentSlot.DEV); Environments.Add(DeploymentSlot.TEST); Environments.Add(DeploymentSlot.BETA); Environments.Add(DeploymentSlot.PROD); } _notificationProvider = notificationProvider; _navigationManager = navigationManager; _authenticationProvider = authenticationProvider; _buildProvider = buildProvider; _eventLogger = eventLogger; LoginCommand = new RelayCommand(Login, () => !IsLogging); UpdatePasswordCommand = new RelayCommand(UpdatePassword, () => IsChangingPassword); cryptographer = new Rfc2898Cryptographer(); Email = _settings.LastLoginEmail; SerialNumber = _settings.LastLoginSerialNumber; DeploymentSlot = _settings.DeploymentSlot; RememberMe = _settings.RememberMe; if (_settings.LastLoginMethod == LoginMethod.ActiveDirectory) { IsActiveDirectory = true; } else { IsStandardUser = true; } if (_buildProvider.BuildType == MSBuildType.Lite) { IsStandardUser = true; } try { Password = cryptographer.Decrypt(_settings.LastLoginPassword); } catch (Exception ex) { LogManager.Log(ex); } } /// /// Logins the requested user. /// private async void Login() { if (Validate()) { try { IsLogging = true; ShowLoggingDetails = false; NewPassword1 = String.Empty; NewPassword2 = String.Empty; InvalidateRelayCommands(); LoginMethod loginMethod = IsActiveDirectory ? LoginMethod.ActiveDirectory : LoginMethod.StandardUser; await Task.Factory.StartNew(() => { _settings.DeploymentSlot = DeploymentSlot; LoginResponse result = _authenticationProvider.Login(Email, Password, loginMethod, _settings.ByPassEnvironmentVersionCheck, (progress) => { ProgressLog = progress; }, SerialNumber).Response; if (result.VersionChangeRequired && !_settings.ByPassEnvironmentVersionCheck) { InvokeUI(() => { if (_notificationProvider.ShowErrorQuestion($"Your version of Machine Studio is different from the version specified for the selected environment.\nMachine Studio must be changed to version '{result.RequiredVersion}' in order to continue.\nDo you wish to update your local version?")) { TangoMessenger.Default.Send(new ChangeVersionMessage() { LoginResponse = result, }); _navigationManager.NavigateTo(NavigationView.UpdateView); } }); return; } if (result.PasswordChangeRequired) { StartUpdatePassword().Task.GetAwaiter().GetResult(); Password = NewPassword1; Login(); return; } //_eventLogger.Log(EventTypes.APPLICATION_STARTED, "Application Started!"); _navigationManager.NavigateTo(NavigationView.MainView); _settings.LastLoginEmail = Email; _settings.RememberMe = RememberMe; _settings.LastLoginMethod = loginMethod; _settings.LastLoginPassword = RememberMe ? cryptographer.Encrypt(Password) : null; _settings.LastLoginSerialNumber = SerialNumber; _settings.Save(); EnableSlotSelection = false; IsLogging = false; ShowLoggingDetails = true; IsChangingPassword = false; InvalidateRelayCommands(); InvokeUI(() => { MainWindow.Instance.Title += $" - {_settings.DeploymentSlot}"; }); }); } catch (Exception ex) { IsLogging = false; ShowLoggingDetails = true; IsChangingPassword = false; InvalidateRelayCommands(); LogManager.Log(ex, "Login Error."); _notificationProvider.ShowError($"An error occurred while trying to perform the log-in operation.\n{ex.FlattenMessage()}"); } } } private TaskCompletionSource StartUpdatePassword() { _updatePasswordCompletionSource = new TaskCompletionSource(); IsChangingPassword = true; ShowLoggingDetails = false; IsLogging = false; InvalidateRelayCommands(); return _updatePasswordCompletionSource; } private async void UpdatePassword() { await Task.Factory.StartNew(() => { try { if (!Validate()) { return; } ProgressLog = "Updating your password..."; IsChangingPassword = false; IsLogging = true; InvalidateRelayCommands(); using (var db = ObservablesContext.CreateDefault()) { var user = db.Users.SingleOrDefault(x => x.Email == Email); user.PasswordChangeRequired = false; user.Password = User.GetPasswordHash(NewPassword1); db.SaveChanges(); } _updatePasswordCompletionSource.SetResult(true); } catch (Exception ex) { IsLogging = false; IsChangingPassword = false; ShowLoggingDetails = true; InvalidateRelayCommands(); _updatePasswordCompletionSource.SetException(ex); } finally { InvalidateRelayCommands(); } }); } protected override void OnValidating() { if (IsChangingPassword) { if (!NewPassword1.IsBetweenLength(6, 8)) { InsertError(nameof(NewPassword1), "Password must be 6 to 8 characters long"); } if (NewPassword1 != NewPassword2) { InsertError(nameof(NewPassword2), "Passwords do not match"); } } base.OnValidating(); } } }