diff --git a/ayanova/devdocs/todo.txt b/ayanova/devdocs/todo.txt index a5e365b8..e5a1be42 100644 --- a/ayanova/devdocs/todo.txt +++ b/ayanova/devdocs/todo.txt @@ -41,11 +41,27 @@ All platforms and browsers - DONE Make about contextual and insert a menu item to view log - DONE WIRE up save menu item and add code to disable save on broken rules (and make red, disabled etc) - DONE Move wire up event code from app.vue to gzmenu and call it from app.vue -### - Need rights in form state so can easily enable / disable etc - - BIG TODO: it would be far nicer if rights to objects were stored in a single JSON fragment that could be easily copied into javascript and c# - - code automatically builds rights collection from json fragment so can use it between both projects and more easily update it in one central spot - - Get that working then come back to the rest of the rights in client side - - Need to create sample users in server project that have all the different widget right combinations for testing purposes +### - RIGHTS in form state so can easily enable / disable etc + - Form (AND THE LIST OBJECT) should check rights and adapt accordingly + - ReadFULL record but no change should show record read only + - Delete should be checked + - CHANGE should be checked + - If no rights then should redirect back to HOME + - If user attempts to load widget form but has no rights to widget server returns the NO rights code but that is triggering back to login + - Should it do this? Normally would not be an issue because it wouldn't open that form unless they saved or manually typed a link to it + - Technically it's incorrect, they don't have to login, they just can't get that record + - Have a look at why it's redirecting and decide if this is worth a change + - DONE BIG TODO: it would be far nicer if rights to objects were stored in a single JSON fragment that could be easily copied into javascript and c# + - DONE code automatically builds rights collection from json fragment so can use it between both projects and more easily update it in one central spot + - DONE Get that working then come back to the rest of the rights in client side + - DONE ALREADY Need to create sample users in server project that have all the different widget right combinations for testing purposes + - ??HOME not localized issue, on login, sometimes the home page is not showing as localized! Some kind of timing issue or wrong event used to localize it or something. ?? + - I see that HOME->BeforeCreate breakpoint is hit **BEFORE** the locale text has been fetched. + - 1) this should never happen and is calling into question my use of promises here + - 2) Make it happen that beforeCreate in home absolutely never gets called before all the init is done + - 3) I think this means that login router push to HOME should not happen until init is done even earlier + - So WTF on that, supposed to await the promise resolution from login then init and stuff, need to sort that, it's out of whack + - Likely I'm not dealing with the promises properly - Wire up delete menu item - api code is stubbed out for delete, need to write that as well - DONE TODO navigating through menu doesn't "back" properly when clicking back on browser controls @@ -63,7 +79,7 @@ All platforms and browsers - DONE: Application name is all lowercase "ayanova" when installed to device, must be something in the manifest files? - Check about page localization code, is it firing in the right place because on Edge and iPad firefox it didn't show localized at first until a refresh so it fetched the keys but didn't display properly - Calendar on iPad in two occasions with ff and opera the calendar date could not be selected until a time was changed then the date worked. Before that it would always stay the same no matter what selection was made and the UI would not show a change on select or press of date either. - - On login, sometimes the home page is not showing as localized! Some kind of timing issue or wrong event used to localize it or something. ?? + - Navigation guard: navigate away with unsaved changes should warn and prevent but have option to continue anyway diff --git a/ayanova/src/api/authorizationroles.js b/ayanova/src/api/authorizationroles.js index 57870421..2c1e47e8 100644 --- a/ayanova/src/api/authorizationroles.js +++ b/ayanova/src/api/authorizationroles.js @@ -1,11 +1,9 @@ /* eslint-disable */ import _ from "../libs/lodash.min.js"; import store from "../store"; -import ayt from "./ayatype"; import rights from "./bizroles"; -export default { - AYATYPE: ayt, +export default { ROLE_RIGHTS: rights, AUTHORIZATION_ROLES: { ///No role set @@ -50,9 +48,7 @@ export default { ///////////////////////////////// // // - getRights(vm, oType, ownerId) { - //NOTE: this is to mirror the functionality of BizRoles.cs where all rights by role are specified in server project - //any change there needs to be mirrored here + getRights(vm, oType, ownerId) { //from bizroles.cs: //HOW THIS WORKS / WHATS EXPECTED @@ -62,7 +58,6 @@ export default { //PICKLIST NOTE: this does not control getting a list of names for selection which is role independent because it's required for so much indirectly //DELETE = SAME AS CHANGE FOR NOW (There is no specific delete right for now though it's checked for by routes in Authorized.cs in case we want to add it in future as a separate right from create.) - //TODO: get this working, then decompose it into several files to make it cleaner var ret = { change: false, read: false, @@ -78,122 +73,24 @@ export default { var objectRoleRights = this.ROLE_RIGHTS[typeName]; //get the logged in user's role var userRole = vm.$store.state.roles; + //see if it's self owned var isSelfOwned = ownerId == vm.$store.state.userId; //calculate the effective rights taking into consideration self owned etc - - //NOTE: for bitwise comparison we do this: - //Desired role to check can be a single role value or the intersection of multiple bits of role values, - //for example if it's a single role then just that number is used (i.e. 2) - //however if its a bunch of roles that can do that operation they need to be intersected (i.e. 2|32|128) which returns a single value for comparison - //and that's how they come from the server so for example a widget change bizrole requires - // Change = AuthorizationRoles.BizAdminFull (enum value 2) | AuthorizationRoles.InventoryFull (enum value 32), these are intersected (2|32) to yield 34 - //now I can compare the user role to 34 to check if either of those two roles are set like this: - //All roles except inventoryfull = 32735 so to be clear it has BizAdminFull which is enough to change a widget, so to check: - // (32735&34) will be nonzero (true), specifically it will calculate to 2 but we don't care about the exact number, just that it isn't zero which - //would indicate that none of the bit fields to check against are set in the user role hence they don't have that right. - //if we need to combine rights just do it like in c# by intersection operator | (2|32) = 34 - //UserCurrentRole & (desiredRole) == 0 or false if no desired role bits set in currentrole or non zero if any of the bits are a match - // - + //a non zero result of the bitwise calculation means true and zero means false so using !! to force it into a boolean value (contrary to some style guides that say !! is obscure but I say it saves a lot of typing) var canChange = !!(userRole & objectRoleRights.Change); - var canEditOwn = !!(userRole & objectRoleRights.EditOwn); + var canEditOwn = isSelfOwned && (!!(userRole & objectRoleRights.EditOwn)); var canReadFullRecord = !!(userRole & objectRoleRights.ReadFullRecord); - //TEST BizAdminLimited, should only be able to read full record, no edit, no change rights - var testUserBizAdminLimited = { - userId: 2, - roles: 1 - }; - - var canChange2 = !!(testUserBizAdminLimited.roles & objectRoleRights.Change); - var canEditOwn2 = !!(testUserBizAdminLimited.roles & objectRoleRights.EditOwn); - var canReadFullRecord2 = !!(testUserBizAdminLimited.roles & objectRoleRights.ReadFullRecord); - - //widget rights required - // Change: 34 - // ​ - // EditOwn: 256 - // ​ - // ReadFullRecord: 17 - - /** - * - - * NoRole = 0, - ///BizAdminLimited - BizAdminLimited = 1, - ///BizAdminFull - BizAdminFull = 2, - ///DispatchLimited - DispatchLimited = 4, - ///DispatchFull - DispatchFull = 8, - ///InventoryLimited - InventoryLimited = 16, - ///InventoryFull - InventoryFull = 32, - ///AccountingFull - AccountingFull = 64,//No limited role, not sure if there is a need - ///TechLimited - TechLimited = 128, - ///TechFull - TechFull = 256, - ///SubContractorLimited - SubContractorLimited = 512, - ///SubContractorFull - SubContractorFull = 1024, - ///ClientLimited - ClientLimited = 2048, - ///ClientFull - ClientFull = 4096, - ///OpsAdminLimited - OpsAdminLimited = 8192, - ///OpsAdminFull - OpsAdminFull = 16384, - - -//////////////////////////////////////////////////////////// - //WIDGET - // - roles.Add(AyaType.Widget, new BizRoleSet() - { - Change = AuthorizationRoles.BizAdminFull (2) | AuthorizationRoles.InventoryFull (32), =34 - EditOwn = AuthorizationRoles.TechFull, = 256 - ReadFullRecord = AuthorizationRoles.BizAdminLimited | AuthorizationRoles.InventoryLimited = 17 - }); - - -//One owner who doesn't control anything but views stuff - GenSeedUser(log, 1, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, timeZoneOffset); -(4|16|8192) = 8212 - - - - -So checking role = eg: InventoryFull === (UserRole && InventoryFull) -But a test shows a user with role 1 bizadminlimited has no rights to readfull record a widget -17&1=1 -So checking a role should be userRole===(UserRole && InventoryFull), lets try it -8212 = user with no rights to Change a widget, what happens if we test it: -8212===(8212&34)= false -InventoryFull User with rights to change a full record: -32===(32&34)=true -OpsAdminFull user with no rights to change a full record -16384===(16384&34)=false!! -User with every single right but the two required for changing a widget: 32733 -let's test it: -32733===(32733&34)=false! Yes. -let's try one more with bizadminfull added to the prior all other rights and confirm it works:32735 -32735===(32735&34)=false NO! This did not work, WTF it returns 2 instead, maybe the number is too large - -No, 2 is ok, it means that's the bit field that matches, if it returned zero that would indicate a non match in any case so... - -3 - -Ok, this is not working as expected, need to figure this out, test it in a c# console just to confirm if there is a difference there between the two platforms when not expected. -After some research I'm probably doing it wrong: + ret.change=canChange || canEditOwn; + ret.delete=ret.change;//FOR NOW + ret.read=canReadFullRecord; + return ret; + } +}; + /* +USING BITWISE OPERATORS CHEAT SHEET //https://codeburst.io/using-javascript-bitwise-operators-in-real-life-f551a731ff5 // Test whether your bit number has a single attribute. '&' ensures // an intersection between them. @@ -230,83 +127,4 @@ if (myBitNumber == (myBitNumber | (HAS_FOO2 | HAS_FOO4))) { if (myBitNumber == (myBitNumber | (HAS_FOO2 | HAS_FOO3 | HAS_FOO4))) { // False } - - - * - */ - - /** - * - * What to do: - * Object is to return ret fully set as per rights - * - * Look up AyaType key name from value (i.e. 2="Widget") - * Use the type key name to find the object in AYANOVA_RIGHTS by key name - * Error if not found of course - * Using the object found check if can do each thing in RET and return RET - * - * To check need to do just like HasRole, i.e. this: role === (store.state.roles & role); - * - * AyaNova7Import: Object { Change: 16384, EditOwn: 0, ReadFullRecord: 0 } -​ -DataFilter: Object { Change: 2, EditOwn: 32767, ReadFullRecord: 32767 } -​ -FormCustom: Object { Change: 2, EditOwn: 0, ReadFullRecord: 32767 } -​ -JobOperations: Object { Change: 16384, EditOwn: 0, ReadFullRecord: 8195 } -​ -License: Object { Change: 16386, EditOwn: 0, ReadFullRecord: 8193 } -​ -Locale: Object { Change: 16386, EditOwn: 0, ReadFullRecord: 32767 } -​ -LogFile: Object { Change: 0, EditOwn: 0, ReadFullRecord: 24576 } -​ -Metrics: Object { Change: 0, EditOwn: 0, ReadFullRecord: 24576 } -​ -ServerState: Object { Change: 16384, EditOwn: 0, ReadFullRecord: 32767 } -​ -User: Object { Change: 2, EditOwn: 0, ReadFullRecord: 1 } -​ -UserOptions: Object { Change: 2, EditOwn: 0, ReadFullRecord: 1 } -​ -Widget: Object { Change: 34, EditOwn: 256, ReadFullRecord: 17 } -​ -: Object { … } -authorizationroles.js:72 -userRole -"32767" -objType -2 - * - */ - - // switch (objType) { - // case ayatype.Widget: - // //WIDGET - // // Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.InventoryFull, - // // EditOwn = AuthorizationRoles.TechFull, - // // ReadFullRecord = AuthorizationRoles.BizAdminLimited | AuthorizationRoles.InventoryLimited - // ret.change = - // this.hasrole(this.AUTHORIZATION_ROLES.BizAdminFull) || - // this.hasrole(this.AUTHORIZATION_ROLES.InventoryFull); - // ret.editOwn = - // objId == store.state.userId && - // this.hasrole(this.AUTHORIZATION_ROLES.TechFull); - // ret.readFull = - // this.hasrole(this.AUTHORIZATION_ROLES.BizAdminLimited) || - // this.hasRole(this.AUTHORIZATION_ROLES.InventoryLimited); - // ret.delete = ret.change || ret.editOwn; - - // // //////////////////////////////////////////////////////////// - - // break; - // default: - // throw new "authorizationroles::rights - not coded for object type "() + - // objType; - // } - return ret; - } -}; -/* -{"User":{"Change":2,"EditOwn":0,"ReadFullRecord":1},"UserOptions":{"Change":2,"EditOwn":0,"ReadFullRecord":1},"Widget":{"Change":34,"EditOwn":256,"ReadFullRecord":17},"ServerState":{"Change":16384,"EditOwn":0,"ReadFullRecord":32767},"License":{"Change":16386,"EditOwn":0,"ReadFullRecord":8193},"LogFile":{"Change":0,"EditOwn":0,"ReadFullRecord":24576},"JobOperations":{"Change":16384,"EditOwn":0,"ReadFullRecord":8195},"AyaNova7Import":{"Change":16384,"EditOwn":0,"ReadFullRecord":0},"Metrics":{"Change":0,"EditOwn":0,"ReadFullRecord":24576},"Locale":{"Change":16386,"EditOwn":0,"ReadFullRecord":32767},"DataFilter":{"Change":2,"EditOwn":32767,"ReadFullRecord":32767},"FormCustom":{"Change":2,"EditOwn":0,"ReadFullRecord":32767}} -*/ +*/ \ No newline at end of file diff --git a/ayanova/src/api/authutil.js b/ayanova/src/api/authutil.js index 8e4f5abe..437b07cb 100644 --- a/ayanova/src/api/authutil.js +++ b/ayanova/src/api/authutil.js @@ -32,6 +32,9 @@ export function processLogin(response) { roles: token["ayanova/roles"] }); + /* eslint-disable-next-line */ + console.log("STEP 2 - PROCESS LOGIN - CALILNG INITIALIZE"); + //Initialize the application initialize(); store.commit( diff --git a/ayanova/src/api/initialize.js b/ayanova/src/api/initialize.js index 9c874aa0..6f2d3775 100644 --- a/ayanova/src/api/initialize.js +++ b/ayanova/src/api/initialize.js @@ -16,11 +16,17 @@ function addNavItem(title, icon, route) { // Initialize the app // on change of authentication status export default function initialize() { + /* eslint-disable-next-line */ + console.log("STEP 3 - INITIALIZE TOP"); if (store.state.authenticated) { + /* eslint-disable-next-line */ + console.log("STEP 4 - INITIALIZE FETCHING LOCALE KEYS"); //Fetch the core localized text keys that will always be required by user locale .fetch(locale.coreKeys) .then(function putFetchedNavItemsInStore() { + /* eslint-disable-next-line */ + console.log("STEP 5 - DONE FETCHING LOCALE KEYS CREATING NAV ITEMS"); //put nav items into store //Everyone has a home addNavItem(locale.get("Home"), "home", "/"); @@ -80,6 +86,8 @@ export default function initialize() { throw error; }); + /* eslint-disable-next-line */ + console.log("STEP 6 - INIT DONE WITH NAV, NOW FETCHING USEROPTIONS SETTINGS"); //CACHE LOCALE SETTINGS api .get("UserOptions/" + store.state.userId) @@ -98,17 +106,19 @@ export default function initialize() { if (res.data.timeZoneOffset != localOffset) { //todo: timezone doesn't match, offer to fix it - alert( - "Time zone offset for this account is set to " + - res.data.timeZoneOffset + - " which doesn't match the local timezone offset of " + - localOffset + - "." - ); + // alert( + // "Time zone offset for this account is set to " + + // res.data.timeZoneOffset + + // " which doesn't match the local timezone offset of " + + // localOffset + + // "." + // ); } //Store offset in locale data locale.timeZoneOffset = res.data.timeZoneOffset; + /* eslint-disable-next-line */ + console.log("STEP 7 - DONE FETCHING LOCALE SETTINGS"); } }) .catch(function handleFetchUserOptionsError(error) { diff --git a/ayanova/src/api/locale.js b/ayanova/src/api/locale.js index f42873cf..a6240f22 100644 --- a/ayanova/src/api/locale.js +++ b/ayanova/src/api/locale.js @@ -12,6 +12,9 @@ export default { return store.state.localeText[key]; }, fetch(keys) { + /* eslint-disable-next-line */ + console.log("LOCALE - TOP OF FETCH(KEYS)"); + return new Promise(function fetchLocaleKeysFromServer(resolve) { //, reject //step 1: build an array of keys that we don't have already @@ -36,6 +39,10 @@ export default { _.forEach(response.data, function commitFetchedLTItemToStore(item) { store.commit("addLocaleText", item); }); + /* eslint-disable-next-line */ + console.log( + "LOCALE - DONE FETCH(KEYS) and stored about to call Resolve()..." + ); resolve(); }); diff --git a/ayanova/src/views/login.vue b/ayanova/src/views/login.vue index 131c1d0e..184c390c 100644 --- a/ayanova/src/views/login.vue +++ b/ayanova/src/views/login.vue @@ -76,9 +76,13 @@ export default { if (this.input.username != "" && this.input.password != "") { this.errorBadCreds = false; var that = this; + /* eslint-disable-next-line */ + console.log("STEP 1 - LOGIN::AUTHENTICATING"); auth .authenticate(this.input.username, this.input.password) .then(() => { + /* eslint-disable-next-line */ + console.log("STEP 8 - LOGIN::AUTHENTICATE COMPLETED - GOING HOME"); this.$router.push({ name: "home" }); }) .catch(function handleCaughtLoginError(error) {