diff --git a/AyaNovaQBI/AuthorizationRoles.cs b/AyaNovaQBI/AuthorizationRoles.cs new file mode 100644 index 0000000..9a86239 --- /dev/null +++ b/AyaNovaQBI/AuthorizationRoles.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AyaNovaQBI +{ + /// + /// Authorization roles + /// + [Flags] + public enum AuthorizationRoles : int + { + //https://stackoverflow.com/questions/8447/what-does-the-flags-enum-attribute-mean-in-c + //MAX 31 (2147483647)!!! or will overflow int and needs to be turned into a long + //Must be a power of two: https://en.wikipedia.org/wiki/Power_of_two + + ///No role set + NoRole = 0, + ///BizAdminRestricted + BizAdminRestricted = 1, + ///BizAdmin + BizAdmin = 2, + ///ServiceRestricted + ServiceRestricted = 4, + ///Service + Service = 8, + ///InventoryRestricted + InventoryRestricted = 16, + ///Inventory + Inventory = 32, + ///Accounting + Accounting = 64,//No limited role, not sure if there is a need + ///TechRestricted + TechRestricted = 128, + ///Tech + Tech = 256, + ///SubContractorRestricted + SubContractorRestricted = 512, //same as tech but restricted by further business rules (more fine grained) + ///SubContractor + SubContractor = 1024,//same as tech limited but restricted by further business rules (more fine grained) + ///ClientRestricted + CustomerRestricted = 2048, + ///Client + Customer = 4096, + ///OpsAdminRestricted + OpsAdminRestricted = 8192, + ///OpsAdmin + OpsAdmin = 16384, + ///Sales + Sales = 32768, + ///SalesRestricted + SalesRestricted = 65536, + + + ///Anyone of any role + All = BizAdminRestricted | BizAdmin | ServiceRestricted | Service | InventoryRestricted | + Inventory | Accounting | TechRestricted | Tech | SubContractorRestricted | + SubContractor | CustomerRestricted | Customer | OpsAdminRestricted | OpsAdmin | Sales | SalesRestricted + + // ,AllInsideUserRoles = BizAdminRestricted | BizAdmin | ServiceRestricted | Service | InventoryRestricted | + // Inventory | Accounting | TechRestricted | Tech | SubContractorRestricted | + // SubContractor | Sales | SalesRestricted | OpsAdminRestricted | OpsAdmin + + + + }//end AuthorizationRoles +} diff --git a/AyaNovaQBI/AyaNovaQBI.csproj b/AyaNovaQBI/AyaNovaQBI.csproj index f0766c2..d3e9e2b 100644 --- a/AyaNovaQBI/AyaNovaQBI.csproj +++ b/AyaNovaQBI/AyaNovaQBI.csproj @@ -75,6 +75,7 @@ auth.cs + Form @@ -91,6 +92,7 @@ tfa.cs + auth.cs diff --git a/AyaNovaQBI/UserType.cs b/AyaNovaQBI/UserType.cs new file mode 100644 index 0000000..21f06e5 --- /dev/null +++ b/AyaNovaQBI/UserType.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AyaNovaQBI +{ + /// + /// AyaNova User types + /// + public enum UserType : int + { + Service = 1, + NotService = 2, + Customer = 3, + HeadOffice = 4, + ServiceContractor = 5 + } +} diff --git a/AyaNovaQBI/auth.cs b/AyaNovaQBI/auth.cs index e6594a6..22a2a79 100644 --- a/AyaNovaQBI/auth.cs +++ b/AyaNovaQBI/auth.cs @@ -42,8 +42,7 @@ namespace AyaNovaQBI { if (!ValidateAndCleanServerAddress()) return; btnLogin.Enabled = btnTest.Enabled = false; - // if (!util.Initialized) - //{ + var result = await util.InitAndConfirmAddressAsync(edServerUrl.Text); if (result != "OK") { @@ -51,15 +50,50 @@ namespace AyaNovaQBI btnLogin.Enabled = btnTest.Enabled = true; return; } - // } - var res = await util.AuthenticateAsync(edUserName.Text, edPassword.Text); - if (!res) + try { - MessageBox.Show("Login failed"); - btnLogin.Enabled = btnTest.Enabled = true; + var res = await util.AuthenticateAsync(edUserName.Text, edPassword.Text); + if (!res) + { + MessageBox.Show("Login failed"); + btnLogin.Enabled = btnTest.Enabled = true; + this.DialogResult = DialogResult.Cancel; + return; + } + }catch(Exception ex) + { +// --------------------------- + +//--------------------------- +//Error attempting to login: + +// POST error, code: 401, route: auth / tfa - authenticate + +//{ "error":{ "code":"2003","message":"ErrorAPI2003"} } + +// Unauthorized + +// POSTED OBJECT: + +// { "pin":"123456","tempToken":"YvIS2IovMIWUYc4NaovwhNcFXviWwEOx4qvpzt8vfQQ"} +// --------------------------- +// OK +// -------------------------- - + + + MessageBox.Show("Error attempting to login:\r\n" + ex.Message); this.DialogResult = DialogResult.Cancel; return; + } + + MessageBox.Show($@"TEST - LOGIN SUCCEEDED: \r\ +AyaNovaUserName: {util.AyaNovaUserName}\r\n +JWT: {util.JWT}\r\n +AyaNovaUserId: {util.AyaNovaUserId}\r\n +AyaNovaUserTranslationId: {util.AyaNovaUserTranslationId}\r\n +AyaNovaUserRoles: {util.AyaNovaUserRoles}\r\n +AyaNovaUserType: {util.AyaNovaUserType}"); btnLogin.Enabled = btnTest.Enabled = true; diff --git a/AyaNovaQBI/util.cs b/AyaNovaQBI/util.cs index 67a8a31..617e56e 100644 --- a/AyaNovaQBI/util.cs +++ b/AyaNovaQBI/util.cs @@ -25,7 +25,13 @@ namespace AyaNovaQBI internal static string GuessClientUrl { get; set; } internal static string JWT { get; set; } - + internal static long AyaNovaUserId { get; set; } + internal static long AyaNovaUserTranslationId { get; set; } + internal static string AyaNovaUserName { get; set; } + internal static AuthorizationRoles AyaNovaUserRoles { get; set; } + internal static UserType AyaNovaUserType { get; set; } + + public static void InitClient() @@ -92,8 +98,8 @@ namespace AyaNovaQBI var InnerErr = ""; if (ex.InnerException != null) InnerErr = ex.InnerException.Message; - return false; - // throw new Exception("POST error, route: " + route + "\r\nError:" + Err + "\r\nInner error:" + InnerErr + "\r\nStack:" + ex.StackTrace + "\r\nPOSTED OBJECT:\r\n" + postJson); + + throw new Exception("Authentication error, route: AUTH\r\nError:" + Err + "\r\nInner error:" + InnerErr); } //ApiResponse a = await PostAsync("auth", creds.ToString()); @@ -107,30 +113,65 @@ namespace AyaNovaQBI //Get temp token from response var tempToken = a.ObjectResponse["data"]["tt"].Value(); - string tfaPin = null; - bool keepTrying = true; + //get 2fa code and send it in do { tfa t = new tfa(); if (t.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) return false; - tfaPin = t.TFAPin; + string tfaPin = t.TFAPin; + dynamic tfaCreds = new JObject(); + tfaCreds.pin = tfaPin; + tfaCreds.tempToken = tempToken; + try + { + var tfaResponse = await TryPostAsync("auth/tfa-authenticate", tfaCreds.ToString(Newtonsoft.Json.Formatting.None));//trypost is no delay + if (ProcessLoginResponse(tfaResponse)) return true; + } + catch(Exception ex) + { + if (!ex.Message.Contains("2003"))//if not an authentication error (bad pin) then throw it back up for display + throw ex; + //otherwise eat it and let them re-enter the pin again to mirror how ayanova web client works + } + - } while (keepTrying); + } while (true); } else { - JWT = a.ObjectResponse["data"]["token"].Value(); - return true; + return ProcessLoginResponse(a); } } return false; } + private static bool ProcessLoginResponse(ApiResponse a) + { + if (a.ObjectResponse == null) return false; + if (!a.HttpResponse.IsSuccessStatusCode) + { + return false; + } + if(a.ObjectResponse["data"]["l"].Value())//license lockout + { + throw new Exception("Server login from QBI is disabled due to AyaNova license issue"); + } + + JWT = a.ObjectResponse["data"]["token"].Value(); + // AyaNovaUserId= a.ObjectResponse["data"]["id"].Value();//nbot here maybe in jwt + //AyaNovaUserTranslationId = a.ObjectResponse["data"]["tid"].Value(); + AyaNovaUserName = a.ObjectResponse["data"]["name"].Value(); + AyaNovaUserRoles = (AuthorizationRoles)(int.Parse(a.ObjectResponse["data"]["roles"].Value())); + AyaNovaUserType = (UserType)(int.Parse(a.ObjectResponse["data"]["usertype"].Value())); + + return true; + } + public async static Task GetAsync(string route)