123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Comal.Classes;
- using InABox.Clients;
- using InABox.Configuration;
- using InABox.Core;
- using InABox.Mobile;
- using InABox.Rpc;
- using Xamarin.Essentials;
- using Xamarin.Forms;
- using XF.Material.Forms.UI;
- using XF.Material.Forms.UI.Dialogs;
- namespace comal.timesheets
- {
- public partial class PinLoginPage : ContentPage
- {
- private enum SubPage
- {
- Autologin,
- Pin,
- TwoFactor,
- Password
- }
-
- private SubPage _page;
- private String _pin = "";
- private String _2fa = "";
-
- private DatabaseSettings _settings { get; set; }
-
- public PinLoginPage()
- {
- InitializeComponent();
- SelectSubPage(SubPage.Autologin);
- }
-
- protected override async void OnAppearing()
- {
- MobileLogging.Log("PINLoginPage.Appearing()");
- base.OnAppearing();
-
- // IS there a new version? If so, then get that before doing anything else
- MobileLogging.Log("PINLoginPage.CheckForLatestVersion()");
- CheckForLatestVersion();
- //TODO this is only for the temporary site app login - needs to be removed
- SelectSubPage(SubPage.Autologin);
- // Is this the first time through?
- // If so, then try to validate
- if (_page == SubPage.Autologin)
- {
-
- MobileLogging.Log("PINLoginPage.CheckAutoConfigurationLink()");
- CheckAutoConfigurationLink();
-
- MobileLogging.Log("PINLoginPage.LoadSettings()");
- LoadSettings();
- MobileLogging.Log("PINLoginPage.Connecting()");
- TransportStatus connection = TransportStatus.None;
- using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Connecting"))
- {
- await Task.Run(() =>
- {
- connection = App.ConnectTransport(_settings.URLs);
- });
-
- }
-
- if (connection != TransportStatus.OK)
- {
- await DisplayAlert("Connection Error", $"Unable to establish a connection!\n\nERR: {connection}", "OK");
- Navigation.PushAsync(new SettingsPage());
- return;
- }
- MobileLogging.Log("PINLoginPage.AutoLogin()");
- ValidationStatus status = ValidationStatus.INVALID;
- using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Validating"))
- {
- Task.Run(() => { status = AutoLogin(); }).Wait();
- }
- if (status == ValidationStatus.VALID)
- {
- MobileLogging.Log("PINLoginPage.CheckPasswordExpiration()");
- if (await CheckPasswordExpiration())
- {
- PasswordCancelColumn.Width = new GridLength(1, GridUnitType.Star);
- SelectSubPage(SubPage.Password);
- }
- else
- {
- MobileLogging.Log("PINLoginPage.LaunchMainPage()");
- LaunchMainPage();
- }
- }
- else if (status == ValidationStatus.INVALID)
- {
-
- // If we _should_ have been able to log in (ie we have a username)
- // show an error message. Otherwise, jump to the PIN page
- if (!String.IsNullOrWhiteSpace(_settings.UserID))
- await DisplayAlert("Error logging in", "Invalid User ID, Password or PIN", "OK");
-
- MobileLogging.Log("PINLoginPage.SelectSubPage(SubPage.Pin)");
- SelectSubPage(SubPage.Pin);
- }
-
- else if (status == ValidationStatus.REQUIRE_2FA)
- {
- MobileLogging.Log("PINLoginPage.SelectSubPage(SubPage.TwoFactor)");
- SelectSubPage(SubPage.TwoFactor);
- }
-
- else if (status == ValidationStatus.PASSWORD_EXPIRED)
- {
- MobileLogging.Log("PINLoginPage.SelectSubPage(SubPage.Password)");
- await DisplayAlert("Alert", "Your password has expired - please change it", "OK");
- PasswordCancelColumn.Width = new GridLength(0, GridUnitType.Absolute);
- SelectSubPage(SubPage.Password);
- }
- }
-
- else if (_page == SubPage.Pin)
- {
- UpdatePIN('\0');
- }
-
- else if (_page == SubPage.TwoFactor)
- {
- // Has the user left the app to go and get the 2fa code from sms or google authenticator?
- // If so, then try to retrieve the 2fa code from the clipboard
- var text = Clipboard.GetTextAsync().Result;
- if ((text?.Length == 6) && (text?.All(Char.IsDigit) == true))
- {
- _2fa = text;
- TwoFA0.Text = text.Substring(0, 1);
- TwoFA1.Text = text.Substring(1, 1);
- TwoFA2.Text = text.Substring(2, 1);
- TwoFA3.Text = text.Substring(3, 1);
- TwoFA4.Text = text.Substring(4, 1);
- TwoFA5.Text = text.Substring(5, 1);
- }
- else
- UpdateTwoFA('\0');
- }
- else if (_page == SubPage.Password)
- {
- NewPassword.Text = "";
- NewPassword2.Text = "";
- ChangePasswordButton.IsEnabled = false;
- }
- }
-
- private void CheckAutoConfigurationLink()
- {
- var parameters = App.LaunchParameters;
- App.LaunchParameters = "";
-
- if (String.IsNullOrWhiteSpace(parameters))
- return;
-
- var decrypted = Encryption.Decrypt(parameters, "logindetailslink");
- if (decrypted?.ToUpper()?.Contains("ENDURLS") == true)
- {
- var comps = decrypted.Split("ENDURLS");
- if (comps.Length == 2)
- {
- var urls = comps.First().Split(',');
- var settings = comps.Last().Split(',');
- if (urls.Any() && (settings.Length == 3))
- {
- String user = settings[0];
- String password = settings[1];
- DateTime expiry = DateTime.Parse(settings[2]);
- if (expiry > DateTime.Now)
- {
- _settings.URLs = urls;
- _settings.UserID = user;
- _settings.Password = password;
- new LocalConfiguration<DatabaseSettings>().Save(_settings);
- DisplayAlert("Autoconfiguration", "Configuration Updated Successfully!!", "OK").Wait();
- }
- DisplayAlert("AutoConfiguration", "AutoConfiguration Link has expired!", "OK").Wait();
- }
- DisplayAlert("AutoConfiguration", "Invalid AutoConfiguration content!", "OK").Wait();
- }
- DisplayAlert("AutoConfiguration", "Invalid AutoConfiguration structure!", "OK").Wait();
- }
- DisplayAlert("AutoConfiguration", "Invalid AutoConfiguration format!", "OK").Wait();
-
- }
- private void LoadSettings()
- {
-
- _settings = new LocalConfiguration<DatabaseSettings>().Load();
- _settings.URLs = new String[]
- {
- "remote.com-al.com.au:8050",
- };
- new LocalConfiguration<DatabaseSettings>().Save(_settings);
- if (!_settings.URLs.Any())
- {
- // Do we have old-style settings we can migrate from?
- var Settings = new LocalConfiguration<ConnectionSettings>().Load();
- if (!String.IsNullOrWhiteSpace(Settings.URL))
- {
- _settings.URLs = new string[] { Settings.URL };
- _settings.UserID = Settings.UserID;
- _settings.Password = Settings.Password;
- // Get rid of the old ConnectionSettings storage
- new LocalConfiguration<ConnectionSettings>().Delete();
- }
- }
- if (!_settings.URLs.Any())
- _settings.URLs = new String[]
- {
- "remote.com-al.com.au:8050",
- };
- if (string.IsNullOrWhiteSpace(_settings.UserID) || string.IsNullOrWhiteSpace(_settings.Password))
- {
- Navigation.PushAsync(new SettingsPage());
- }
-
- // Still nothing? Populate with Defaults
- if (!_settings.URLs.Any())
- {
- _settings.URLs = new String[]
- {
- "remote.com-al.com.au:8050",
- //"192.168.0.247:8010"
- //"demo.prsdigital.com.au:8003",
- //"demo2.prsdigital.com.au:8003",
- //"demo3.prsdigital.com.au:8003"
- };
- //_settings.UserID = "frank";
- //_settings.Password = "frankvb22";
- _settings.UserID = "GUEST";
- _settings.Password = "guest";
- //_settings.UserID = "ADMIN";
- //_settings.Password = "admin";
- new LocalConfiguration<DatabaseSettings>().Save(_settings);
- }
- //TODO - re enable this when this app is not just the site module
- //App.IsSharedDevice = String.IsNullOrWhiteSpace(_settings.UserID);
- }
-
- public ValidationStatus AutoLogin(string pin = "")
- {
-
- ValidationStatus result = ValidationStatus.INVALID;
- Guid sessionID = App.Current.Properties.ContainsKey("SessionID")
- ? Guid.Parse(App.Current.Properties["SessionID"].ToString())
- : Guid.Empty;
-
- if (!Guid.Equals(sessionID, Guid.Empty))
- result = ClientFactory.Validate(sessionID);
- if (result == ValidationStatus.INVALID)
- {
- if (!String.IsNullOrWhiteSpace(_settings.UserID) && !String.IsNullOrWhiteSpace(_settings.Password))
- result = ClientFactory.Validate(_settings.UserID, _settings.Password);
- }
-
- return result;
-
- }
-
- private async Task<bool> CheckPasswordExpiration()
- {
- if (!ClientFactory.PasswordExpiration.IsEmpty())
- {
- var timeUntilExpiration = ClientFactory.PasswordExpiration - DateTime.Now;
- if (timeUntilExpiration.Days < 14)
- {
- string chosenOption = await DisplayActionSheet("Alert",
- $"Password will expire in {timeUntilExpiration.Days} days. Change password now?", null, "Yes", "No");
- return String.Equals(chosenOption, "Yes");
- }
- }
- return false;
- }
- private async void LaunchMainPage()
- {
- Device.BeginInvokeOnMainThread(() => SelectSubPage(String.IsNullOrWhiteSpace(_settings.UserID) ? SubPage.Pin : SubPage.Autologin));
- using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Starting Up"))
- App.Data.Setup();
- Navigation.PushAsync(new SiteModule());
- }
-
- private void CheckForLatestVersion()
- {
- Task.Run(async () =>
- {
- try
- {
- bool isLatest = true;
- try
- {
- isLatest = await MobileUtils.AppVersion.IsUsingLatestVersion();
- }
- catch (Exception eLatest)
- {
- string s = eLatest.Message;
- }
- if (!isLatest)
- {
- string latestVersionNumber = await MobileUtils.AppVersion.GetLatestVersionNumber();
- Device.BeginInvokeOnMainThread(async () =>
- {
- string chosenOption = await DisplayActionSheet(String.Format("Version {0} Available. Update now?", latestVersionNumber), "You will be reminded again in 10 minutes.", null, "Yes", "No");
- switch (chosenOption)
- {
- case "No":
- break;
- case "Cancel":
- break;
- case "Yes":
- Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); });
- break;
- default:
- break;
- }
- });
- }
- }
- catch { }
- });
- }
-
- private void SelectSubPage(SubPage page)
- {
- _page = page;
-
- SplashLayout.IsVisible = _page == SubPage.Autologin;
- PINLayout.IsVisible = _page == SubPage.Pin;
- TwoFALayout.IsVisible = _page == SubPage.TwoFactor;
- PasswordLayout.IsEnabled = _page == SubPage.Password;
-
- MasterGrid.ColumnDefinitions[0].Width = _page == SubPage.Autologin
- ? new GridLength(1, GridUnitType.Star)
- : new GridLength(0, GridUnitType.Absolute);
-
- MasterGrid.ColumnDefinitions[1].Width = _page == SubPage.Pin
- ? new GridLength(1, GridUnitType.Star)
- : new GridLength(0, GridUnitType.Absolute);
-
- MasterGrid.ColumnDefinitions[2].Width = _page == SubPage.TwoFactor
- ? new GridLength(1, GridUnitType.Star)
- : new GridLength(0, GridUnitType.Absolute);
-
- UpdatePIN('\0');
- UpdateTwoFA('\0');
-
- NewPassword.Text = "";
- NewPassword2.Text = "";
- ChangePasswordButton.IsEnabled = false;
-
- }
-
- #region PIN Page
-
- private void UpdatePIN(char digit)
- {
- _pin = digit switch
- {
- '\0' => "",
- '\b' => _pin.Length > 0 ? _pin.Substring(0, _pin.Length - 1) : "",
- _ => String.Concat(_pin, digit)
- };
- Pin0.Text = _pin.Length > 0 ? "*" : "";
- Pin1.Text = _pin.Length > 1 ? "*" : "";
- Pin2.Text = _pin.Length > 2 ? "*" : "";
- Pin3.Text = _pin.Length > 3 ? "*" : "";
- PinBack.IsEnabled = !String.IsNullOrEmpty(_pin);
- PinOK.IsEnabled = _pin?.Length == 4;
- }
- void Pin_Button_Click(object sender, EventArgs args)
- {
- UpdatePIN((sender as Button).Text.ToCharArray().First());
- }
-
- void Pin_Back_Click(object sender, EventArgs args)
- {
- UpdatePIN('\b');
- }
- async void Pin_OK_Click(object sender, EventArgs args)
- {
- var status = ClientFactory.Validate(_pin);
- if (status == ValidationStatus.INVALID)
- {
- DisplayAlert("PIN Error", "Invalid PIN Entered!", "OK").Wait();
- UpdatePIN('\0');
- }
- else if (status == ValidationStatus.REQUIRE_2FA)
- SelectSubPage(SubPage.TwoFactor);
- else
- LaunchMainPage();
- }
-
- #endregion
-
- #region Two Factor Page
-
- private void UpdateTwoFA(char digit)
- {
- _2fa = digit switch
- {
- '\0' => "",
- '\b' => _2fa.Length > 0 ? _2fa.Substring(0, _2fa.Length - 1) : "",
- _ => String.Concat(_2fa, digit)
- };
- TwoFA0.Text = _2fa.Length > 0 ? _2fa.Substring(0,1) : "";
- TwoFA1.Text = _2fa.Length > 1 ? _2fa.Substring(1,1) : "";
- TwoFA2.Text = _2fa.Length > 2 ? _2fa.Substring(2,1) : "";
- TwoFA3.Text = _2fa.Length > 3 ? _2fa.Substring(3,1) : "";
- TwoFA4.Text = _2fa.Length > 4 ? _2fa.Substring(4,1) : "";
- TwoFA5.Text = _2fa.Length > 5 ? _2fa.Substring(5,1) : "";
- TwoFABack.IsEnabled = !String.IsNullOrEmpty(_2fa);
- TwoFAOK.IsEnabled = _2fa?.Length == 6;
- }
- void TwoFA_Button_Click(object sender, EventArgs args)
- {
- UpdateTwoFA((sender as Button).Text.ToCharArray().First());
- }
-
- void TwoFA_Back_Click(object sender, EventArgs args)
- {
- UpdateTwoFA('\b');
- }
- void TwoFA_OK_Click(object sender, EventArgs args)
- {
- if (ClientFactory.Check2FA(_2fa))
- LaunchMainPage();
- DisplayAlert("2FA Error", "Invalid 2FA Code", "OK").Wait();
- UpdateTwoFA('\0');
- }
-
- #endregion
-
- #region Change Password Page
-
- private void NewPassword_OnTextChanged(object sender, TextChangedEventArgs e)
- {
- ChangePasswordButton.IsEnabled =
- !String.IsNullOrWhiteSpace(NewPassword.Text)
- && NewPassword.Text.Length >= 5
- && !String.IsNullOrWhiteSpace(NewPassword2.Text)
- && String.Equals(NewPassword.Text, NewPassword2.Text);
- }
-
- private async void ChangePasswordButton_OnClicked(object sender, EventArgs e)
- {
- using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Saving"))
- {
- var user = new User() { ID = ClientFactory.UserGuid };
- user.Password = NewPassword.Text;
- new Client<User>().Save(user, "Password updated by User from Timebench");
- _settings.Password = user.Password;
- new LocalConfiguration<DatabaseSettings>().Save(_settings);
-
- LaunchMainPage();
- }
- }
-
- private void CancelPasswordButton_OnClicked(object sender, EventArgs e)
- {
- LaunchMainPage();
- }
-
- #endregion
- private void Settings_OnClicked(object sender, EventArgs e)
- {
- // Always try autologin when returning from Settings Page
- SelectSubPage(SubPage.Autologin);
- Navigation.PushAsync(new SettingsPage());
- }
-
- }
- }
|