Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: single sign on functionality #221

Merged
merged 4 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ VUE_APP_ALIAS={}
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_MAPPING_TYPES={"PO": "PO_MAPPING_PREF","RSTINV": "INV_MAPPING_PREF"}
VUE_APP_MAPPING_PO={"orderId": { "label": "Order ID", "required": true }, "productSku": { "label": "Shopify product SKU", "required": true },"orderDate": { "label": "Arrival date", "required": true }, "quantity": { "label": "Ordered quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }}
VUE_APP_MAPPING_RSTINV={"productSku": { "label": "Product SKU", "required": true }, "quantity": { "label": "Quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }, "locationSeqId": { "label": "Facility Location", "required": true }}
VUE_APP_MAPPING_RSTINV={"productSku": { "label": "Product SKU", "required": true }, "quantity": { "label": "Quantity", "required": true }, "facility": { "label": "Facility ID", "required": true }, "locationSeqId": { "label": "Facility Location", "required": true }}
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
492 changes: 288 additions & 204 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@capacitor/core": "^2.4.7",
"@hotwax/app-version-info": "^1.0.0",
"@hotwax/apps-theme": "^1.1.0",
"@hotwax/dxp-components": "^1.3.4",
"@hotwax/oms-api": "^1.6.0",
"@ionic/core": "6.7.5",
"@ionic/vue": "6.7.5",
Expand Down
3 changes: 2 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export default defineComponent({
},
async unauthorized() {
this.store.dispatch("user/logout");
this.router.push("/login")
const redirectUrl = window.location.origin + '/login';
window.location.href = `${process.env.VUE_APP_LOGIN_URL}?redirectUrl=${redirectUrl}`;
}
},
async mounted() {
Expand Down
3 changes: 3 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"Are you sure you want to delete this CSV mapping? This action cannot be undone.": "Are you sure you want to delete this CSV mapping? This action cannot be undone.",
"Are you sure you want to update this CSV mapping? This action cannot be undone.": "Are you sure you want to update this CSV mapping? This action cannot be undone.",
"Arrival date": "Arrival date",
"Authenticating": "Authenticating",
"Backorder": "Backorder",
"Blank": "Blank",
"Buffer days": "Buffer days",
Expand Down Expand Up @@ -52,6 +53,7 @@
"Changes to the CSV mapping has been saved.": "Changes to the CSV mapping has been saved.",
"File upload": "File upload",
"File uploaded successfully": "File uploaded successfully",
"Go to Launchpad": "Go to Launchpad",
"Go to OMS": "Go to OMS",
"here": "here",
"items": "items",
Expand All @@ -68,6 +70,7 @@
"line items": "line items",
"Loading": "Loading",
"Login": "Login",
"Logging in": "Logging in",
"Logout": "Logout",
"Mapping": "Mapping",
"Mapping details": "Mapping details",
Expand Down
10 changes: 9 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import store from './store'
import { DateTime } from 'luxon';

import logger from './logger';
import { dxpComponents } from '@hotwax/dxp-components'
import { login, logout, loader } from './user-utils';

const app = createApp(App)
.use(IonicVue, {
Expand All @@ -40,7 +42,13 @@ const app = createApp(App)
})
.use(router)
.use(i18n)
.use(store);
.use(store)
.use(dxpComponents, {
login,
logout,
loader,
appLoginUrl: process.env.VUE_APP_LOGIN_URL as string
});

// Filters are removed in Vue 3 and global filter introduced https://v3.vuejs.org/guide/migration/filters.html#global-filters
app.config.globalProperties.$filters = {
Expand Down
25 changes: 15 additions & 10 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,31 @@ import PurchaseOrder from '@/views/PurchaseOrder.vue'
import Inventory from '@/views/Inventory.vue'
import InventoryReview from '@/views/InventoryReview.vue'
import PurchaseOrderReview from '@/views/PurchaseOrderReview.vue';
import Login from '@/views/Login.vue'
import SavedMappings from '@/views/SavedMappings.vue'
import Settings from "@/views/Settings.vue"
import store from '@/store'
import MappingDetail from '@/views/MappingDetail.vue'
import { Login, useAuthStore } from '@hotwax/dxp-components';
import { loader } from '@/user-utils';

const authGuard = (to: any, from: any, next: any) => {
if (store.getters['user/isAuthenticated']) {
next()
} else {
next("/login")
const authGuard = async (to: any, from: any, next: any) => {
const authStore = useAuthStore()
if (!authStore.isAuthenticated || !store.getters['user/isAuthenticated']) {
await loader.present('Authenticating')
// TODO use authenticate() when support is there
const redirectUrl = window.location.origin + '/login'
window.location.href = `${process.env.VUE_APP_LOGIN_URL}?redirectUrl=${redirectUrl}`
loader.dismiss()
}
next()
};

const loginGuard = (to: any, from: any, next: any) => {
if (!store.getters['user/isAuthenticated']) {
next()
} else {
next("/")
const authStore = useAuthStore()
if (authStore.isAuthenticated && !to.query?.token && !to.query?.oms) {
next('/')
}
next();
};

const routes: Array<RouteRecordRaw> = [
Expand Down
80 changes: 36 additions & 44 deletions src/store/modules/user/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,48 @@ import { hasError, showToast } from '@/utils'
import { translate } from '@/i18n'
import { updateInstanceUrl, updateToken, resetConfig } from '@/adapter'
import logger from "@/logger";
import { useAuthStore } from '@hotwax/dxp-components';

const actions: ActionTree<UserState, RootState> = {

/**
* Login user and return token
*/
async login ({ commit, dispatch }, { username, password }) {
async login ({ commit, dispatch }, payload) {
try {
const resp = await UserService.login(username, password)
if (resp.status === 200 && resp.data) {
if (resp.data.token) {
const permissionId = process.env.VUE_APP_PERMISSION_ID;
if (permissionId) {
const checkPermissionResponse = await UserService.checkPermission({
data: {
permissionId
},
headers: {
Authorization: 'Bearer ' + resp.data.token,
'Content-Type': 'application/json'
}
});

if (checkPermissionResponse.status === 200 && !hasError(checkPermissionResponse) && checkPermissionResponse.data && checkPermissionResponse.data.hasPermission) {
commit(types.USER_TOKEN_CHANGED, { newToken: resp.data.token })
updateToken(resp.data.token)
dispatch('getProfile')
dispatch('setPreferredDateTimeFormat', process.env.VUE_APP_DATE_FORMAT ? process.env.VUE_APP_DATE_FORMAT : 'MM/dd/yyyy');
if (resp.data._EVENT_MESSAGE_ && resp.data._EVENT_MESSAGE_.startsWith("Alert:")) {
// TODO Internationalise text
showToast(translate(resp.data._EVENT_MESSAGE_));
}
return resp.data;
} else {
const permissionError = 'You do not have permission to access the app.';
showToast(translate(permissionError));
logger.error("error", permissionError);
return Promise.reject(new Error(permissionError));
const { token, oms } = payload;
dispatch("setUserInstanceUrl", oms);

if (token) {
const permissionId = process.env.VUE_APP_PERMISSION_ID;
if (permissionId) {
const checkPermissionResponse = await UserService.checkPermission({
data: {
permissionId
},
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
}
} else {
commit(types.USER_TOKEN_CHANGED, { newToken: resp.data.token })
updateToken(resp.data.token)
dispatch('getProfile')
});

if (checkPermissionResponse.status === 200 && !hasError(checkPermissionResponse) && checkPermissionResponse.data && checkPermissionResponse.data.hasPermission) {
commit(types.USER_TOKEN_CHANGED, { newToken: token })
updateToken(token)
await dispatch('getProfile')
dispatch('setPreferredDateTimeFormat', process.env.VUE_APP_DATE_FORMAT ? process.env.VUE_APP_DATE_FORMAT : 'MM/dd/yyyy');
return resp.data;
} else {
const permissionError = 'You do not have permission to access the app.';
showToast(translate(permissionError));
logger.error("error", permissionError);
return Promise.reject(new Error(permissionError));
}
} else if (hasError(resp)) {
showToast(translate('Sorry, your username or password is incorrect. Please try again.'));
logger.error("error", resp.data._ERROR_MESSAGE_);
return Promise.reject(new Error(resp.data._ERROR_MESSAGE_));
} else {
commit(types.USER_TOKEN_CHANGED, { newToken: token })
updateToken(token)
await dispatch('getProfile')
dispatch('setPreferredDateTimeFormat', process.env.VUE_APP_DATE_FORMAT ? process.env.VUE_APP_DATE_FORMAT : 'MM/dd/yyyy');
}
} else {
showToast(translate('Something went wrong'));
logger.error("error", resp.data._ERROR_MESSAGE_);
return Promise.reject(new Error(resp.data._ERROR_MESSAGE_));
}
} catch (err: any) {
showToast(translate('Something went wrong'));
Expand All @@ -75,6 +62,8 @@ const actions: ActionTree<UserState, RootState> = {
* Logout user
*/
async logout ({ commit }) {
const authStore = useAuthStore()

// TODO add any other tasks if need
commit(types.USER_END_SESSION)
resetConfig();
Expand All @@ -83,6 +72,9 @@ const actions: ActionTree<UserState, RootState> = {
// clearing field mappings and current mapping when the user logout
commit(types.USER_FIELD_MAPPINGS_UPDATED, {})
commit(types.USER_CURRENT_FIELD_MAPPING_UPDATED, {id: '', mappingType: '', name: '', value: {}})

// reset plugin state on logout
authStore.$reset()
},

/**
Expand Down
34 changes: 34 additions & 0 deletions src/user-utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { translate } from '@/i18n'
import store from '@/store'
import { loadingController } from '@ionic/vue'

const login = async (payload: any) => store.dispatch('user/login', payload);

const logout = async () => store.dispatch('user/logout');

const loader = {
value: null as any,
present: async (message: string) => {
if (!loader.value) {
loader.value = await loadingController
.create({
message: translate(message),
translucent: false,
backdropDismiss: false
});
}
loader.value.present();
},
dismiss: () => {
if (loader.value) {
loader.value.dismiss();
loader.value = null as any;
}
}
}

export {
login,
loader,
logout
}
17 changes: 14 additions & 3 deletions src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@
<ion-avatar slot="start" v-if="userProfile?.partyImageUrl">
<Image :src="userProfile.partyImageUrl"/>
</ion-avatar>
<ion-card-header>
<!-- ion-no-padding to remove extra side/horizontal padding as additional padding
is added on sides from ion-item and ion-padding-vertical to compensate the removed
vertical padding -->
<ion-card-header class="ion-no-padding ion-padding-vertical">
<ion-card-subtitle>{{ userProfile?.userLoginId }}</ion-card-subtitle>
<ion-card-title>{{ userProfile?.partyName }}</ion-card-title>
</ion-card-header>
</ion-item>
<ion-button fill="outline" color="danger" @click="logout()">{{ $t("Logout") }}</ion-button>
<ion-button color="danger" @click="logout()">{{ $t("Logout") }}</ion-button>
<ion-button fill="outline" @click="goToLaunchpad()">
{{ $t("Go to Launchpad") }}
<ion-icon slot="end" :icon="openOutline" />
</ion-button>
<!-- Commenting this code as we currently do not have reset password functionality -->
<!-- <ion-button fill="outline" color="medium">{{ $t("Reset password") }}</ion-button> -->
</ion-card>
Expand Down Expand Up @@ -190,9 +197,13 @@ export default defineComponent({
},
logout () {
this.store.dispatch('user/logout').then(() => {
this.router.push('/login');
const redirectUrl = window.location.origin + '/login'
window.location.href = `${process.env.VUE_APP_LOGIN_URL}?isLoggedOut=true&redirectUrl=${redirectUrl}`
})
},
goToLaunchpad() {
window.location.href = `${process.env.VUE_APP_LOGIN_URL}`
},
getDateTime(time: any) {
return DateTime.fromMillis(time).toLocaleString(DateTime.DATETIME_MED);
},
Expand Down
3 changes: 2 additions & 1 deletion vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ module.exports = {
fullInstall: true,
enableInSFC: true
}
}
},
runtimeCompiler: true
}