Skip to content

Commit

Permalink
Merge pull request #59 from amansinghbais/user-management/#58
Browse files Browse the repository at this point in the history
Implemented: User permissions for users with different security groups (#58)
  • Loading branch information
ravilodhi authored Dec 21, 2023
2 parents b6e6e78 + 6a04c1d commit 83d2e72
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 21 deletions.
5 changes: 3 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ VUE_APP_I18N_LOCALE=en
VUE_APP_I18N_FALLBACK_LOCALE=en
VUE_APP_CACHE_MAX_AGE=3600
VUE_APP_VIEW_SIZE=20
VUE_APP_PERMISSION_ID=
VUE_APP_PERMISSION_ID="USERS_APP_VIEW"
VUE_APP_ALIAS={}
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
VUE_APP_EXCLUDED_SECURITY_GROUPS=["WAREHOUSE_MANAGER", "STORE_ASSOCIATE", "SECURITY_ADMIN", "COMMERCEUSER"]
11 changes: 10 additions & 1 deletion src/authorization/Actions.ts
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
export default {}
export default {
'APP_UPDT_FULFILLMENT_FACILITY': 'APP_UPDT_FULFILLMENT_FACILITY',
'APP_UPDT_PICKER_CONFG': 'APP_UPDT_PICKER_CONFG',
'APP_UPDT_BLOCK_LOGIN': 'APP_UPDT_BLOCK_LOGIN',
'APP_UPDT_PASSWORD': 'APP_UPDT_PASSWORD',
'APP_UPDT_PRODUCT_STORE_CONFG': 'APP_UPDT_PRODUCT_STORE_CONFG',
'APP_SUPER_USER': 'APP_SUPER_USER',
'APP_SECURITY_GROUP_CREATE': 'APP_SECURITY_GROUP_CREATE',
'APP_USER_CREATE': 'APP_USER_CREATE'
}
12 changes: 11 additions & 1 deletion src/authorization/Rules.ts
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
export default {} as any
export default {
'APP_UPDT_FULFILLMENT_FACILITY': 'STOREFULFILLMENT_ADMIN',
'APP_UPDT_PICKER_CONFG': 'STOREFULFILLMENT_ADMIN',
'APP_USER_CREATE': 'SECURITY_CREATE OR SECURITY_ADMIN',
'APP_UPDT_BLOCK_LOGIN': 'SECURITY_CREATE OR SECURITY_ADMIN',
'APP_UPDT_PASSWORD': 'SECURITY_CREATE OR SECURITY_ADMIN',
'APP_UPDT_PRODUCT_STORE_CONFG': 'SECURITY_CREATE OR SECURITY_ADMIN',
'APP_SECURITY_GROUP_CREATE': 'SECURITY_CREATE OR SECURITY_ADMIN',
'APP_SUPER_USER': 'WEBTOOLS_VIEW',
'USERS_APP_VIEW': 'USERS_APP_VIEW'
} as any
7 changes: 5 additions & 2 deletions src/components/ResetPasswordModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

<!-- TODO improve disable button logic -->
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button :disabled="checkResetButtonStatus()" @click="resetPassword()">
<ion-fab-button :disabled="!hasPermission(Actions.APP_UPDT_PASSWORD) || checkResetButtonStatus()" @click="resetPassword()">
<ion-icon :icon="lockClosedOutline" />
</ion-fab-button>
</ion-fab>
Expand Down Expand Up @@ -83,6 +83,7 @@ import { translate } from '@hotwax/dxp-components'
import { isValidPassword, showToast } from "@/utils";
import { hasError } from "@/adapter";
import { UserService } from "@/services/UserService";
import { Actions, hasPermission } from '@/authorization'
export default defineComponent({
name: "CustomFieldModal",
Expand Down Expand Up @@ -189,10 +190,12 @@ export default defineComponent({
closeOutline,
eyeOutline,
eyeOffOutline,
hasPermission,
lockClosedOutline,
mailOutline,
store,
translate
translate,
Actions
};
},
});
Expand Down
1 change: 1 addition & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"Setup Manually": "Setup Manually",
"Something went wrong": "Something went wrong",
"Status": "Status",
"Super": "Super",
"Timezone": "Timezone",
"The timezone you select is used to ensure automations you schedule are always accurate to the time you select.": "The timezone you select is used to ensure automations you schedule are always accurate to the time you select.",
"This is the name of the OMS you are connected to right now. Make sure that you are connected to the right instance before proceeding.": "This is the name of the OMS you are connected to right now. Make sure that you are connected to the right instance before proceeding.",
Expand Down
15 changes: 12 additions & 3 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,30 @@ const routes: Array<RouteRecordRaw> = [
path: '/create-user',
name: 'CreateUser',
component: CreateUser,
beforeEnter: authGuard
beforeEnter: authGuard,
meta: {
permissionId: "APP_USER_CREATE"
}
},
{
path: '/user-confirmation/:partyId',
name: 'UserConfirmation',
component: UserConfirmation,
beforeEnter: authGuard,
props: true
props: true,
meta: {
permissionId: "APP_USER_CREATE"
}
},
{
path: '/user-quick-setup/:partyId',
name: 'UserQuickSetup',
component: UserQuickSetup,
beforeEnter: authGuard,
props: true
props: true,
meta: {
permissionId: "APP_USER_CREATE"
}
}
]

Expand Down
3 changes: 3 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import RootState from './RootState'
import createPersistedState from "vuex-persistedstate";
import userModule from './modules/user';
import utilModule from "./modules/util"
import { setPermissions } from '@/authorization'

// TODO check how to register it from the components only
// Handle same module registering multiple time on page refresh
Expand All @@ -31,6 +32,8 @@ const store = createStore<RootState>({
},
})

setPermissions(store.getters['user/getUserPermissions']);

export default store
export function useStore(): typeof store {
return useVuexStore()
Expand Down
4 changes: 2 additions & 2 deletions src/store/modules/user/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ const actions: ActionTree<UserState, RootState> = {
if (permissionId) {
// As the token is not yet set in the state passing token headers explicitly
// TODO Abstract this out, how token is handled should be part of the method not the callee
const hasPermission = appPermissions.some((appPermissionId: any) => appPermissionId === permissionId);
const hasPermission = appPermissions.some((appPermission: any) => appPermission.action === permissionId);
// If there are any errors or permission check fails do not allow user to login
if (hasPermission) {
if (!hasPermission) {
const permissionError = 'You do not have permission to access the app.';
showToast(translate(permissionError));
console.error("error", permissionError);
Expand Down
9 changes: 6 additions & 3 deletions src/views/FindUsers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@
</main>
</div>

<ion-fab vertical="bottom" horizontal="end" slot="fixed" @click="router.push('/create-user')">
<ion-fab-button>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button :disabled="!hasPermission(Actions.APP_USER_CREATE)" @click="router.push('/create-user')">
<ion-icon :icon="addOutline" />
</ion-fab-button>
</ion-fab>
Expand Down Expand Up @@ -139,6 +139,7 @@ import { DateTime } from 'luxon';
import UserPopover from '@/components/UserPopover.vue';
import { translate } from '@hotwax/dxp-components'
import FilterMenu from '@/components/FilterMenu.vue';
import { Actions, hasPermission } from '@/authorization'
export default defineComponent({
name: 'FindUsers',
Expand Down Expand Up @@ -224,12 +225,14 @@ export default defineComponent({
addOutline,
cloudyNightOutline,
ellipsisVerticalOutline,
hasPermission,
idCardOutline,
optionsOutline,
toggleOutline,
translate,
router,
store
store,
Actions
};
}
});
Expand Down
32 changes: 25 additions & 7 deletions src/views/UserDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
</ion-item>
<ion-item>
<ion-label>{{ translate("Block login") }}</ion-label>
<ion-toggle slot="end" @click="updateUserLoginStatus($event)" :checked="selectedUser.enabled === 'N'" />
<ion-toggle :disabled="!hasPermission(Actions.APP_UPDT_BLOCK_LOGIN)" slot="end" @click="updateUserLoginStatus($event)" :checked="selectedUser.enabled === 'N'" />
</ion-item>
</ion-list>
<ion-button @click="resetPassword()" fill="outline" color="warning" expand="block">
Expand Down Expand Up @@ -133,8 +133,9 @@
<ion-item>
<ion-icon :icon="businessOutline" slot="start" />
<ion-label>{{ translate('Security Group') }}</ion-label>
<ion-select interface="popover" :disabled="!selectedUser.userLoginId" :value="selectedUser.securityGroup?.groupId" @ionChange="updateSecurityGroup($event)">
<ion-select-option v-for="securityGroup in securityGroups" :key="securityGroup.groupId" :value="securityGroup.groupId">
<ion-label v-if="!hasPermission(Actions.APP_SUPER_USER) && selectedUser.securityGroup?.groupId === 'SUPER'" slot="end">{{ translate('Super') }}</ion-label>
<ion-select v-else interface="popover" :disabled="!hasPermission(Actions.APP_SECURITY_GROUP_CREATE) || !selectedUser.userLoginId" :value="selectedUser.securityGroup?.groupId" @ionChange="updateSecurityGroup($event)">
<ion-select-option v-for="securityGroup in getSecurityGroups(securityGroups)" :key="securityGroup.groupId" :value="securityGroup.groupId">
{{ securityGroup.groupName }}
</ion-select-option>
<ion-select-option value="">{{ translate("None") }}</ion-select-option>
Expand All @@ -147,12 +148,12 @@
<ion-list v-else>
<ion-list-header color="light">
<ion-label>{{ translate('Product stores') }}</ion-label>
<ion-button @click="selectProductStore()">
<ion-button :disabled="!hasPermission(Actions.APP_UPDT_PRODUCT_STORE_CONFG)" @click="selectProductStore()">
{{ translate('Add') }}
<ion-icon slot="end" :icon="addCircleOutline" />
</ion-button>
</ion-list-header>
<ion-item v-for="store in userProductStores" :key="store.productStoreId">
<ion-item :disabled="!hasPermission(Actions.APP_UPDT_PRODUCT_STORE_CONFG)" v-for="store in userProductStores" :key="store.productStoreId">
<ion-label>
<h2>{{ store.storeName }}</h2>
<p>{{ getRoleTypeDesc(store.roleTypeId) }}</p>
Expand All @@ -172,9 +173,9 @@
<ion-list>
<ion-item>
<ion-label>{{ translate("Show as picker") }}</ion-label>
<ion-toggle slot="end" @click="updatePickerRoleStatus($event)" :checked="selectedUser.isWarehousePicker === true" />
<ion-toggle slot="end" :disabled="!hasPermission(Actions.APP_UPDT_PICKER_CONFG)" @click="updatePickerRoleStatus($event)" :checked="selectedUser.isWarehousePicker === true" />
</ion-item>
<ion-item lines="none" button detail @click="selectFacility()">
<ion-item lines="none" button detail :disabled="!hasPermission(Actions.APP_UPDT_FULFILLMENT_FACILITY)" @click="selectFacility()">
<ion-label>{{ selectedUser.facilities.length === 1 ? translate('Added to 1 facility') : translate('Added to facilities', { count: selectedUser.facilities.length }) }}</ion-label>
</ion-item>
</ion-list>
Expand Down Expand Up @@ -234,6 +235,7 @@ import { UserService } from "@/services/UserService";
import { isValidEmail, isValidPassword, showToast } from "@/utils";
import { hasError } from '@/adapter';
import { DateTime } from "luxon";
import { Actions, hasPermission } from '@/authorization'
export default defineComponent({
name: "UserDetails",
Expand Down Expand Up @@ -714,6 +716,20 @@ export default defineComponent({
console.error(error)
}
},
getSecurityGroups(securityGroups: any) {
const excludedSecurityGroups = JSON.parse(process.env.VUE_APP_EXCLUDED_SECURITY_GROUPS)
const selectedSecurityGroup = this.selectedUser.securityGroup.groupId
if(!hasPermission(Actions.APP_SUPER_USER)) excludedSecurityGroups.push('SUPER')
// We have some excluded security groups that can't be created by any users,
// But if a user exists of these excluded security groups, we will show them in the select option.
if(excludedSecurityGroups.includes(selectedSecurityGroup)) {
excludedSecurityGroups.splice(excludedSecurityGroups.indexOf(selectedSecurityGroup), 1)
}
return securityGroups.filter((group: any) => !excludedSecurityGroups.includes(group.groupId))
}
},
setup() {
const router = useRouter();
Expand All @@ -724,11 +740,13 @@ export default defineComponent({
businessOutline,
callOutline,
ellipsisVerticalOutline,
hasPermission,
mailOutline,
router,
store,
translate,
warningOutline,
Actions
}
}
})
Expand Down

0 comments on commit 83d2e72

Please sign in to comment.