diff --git a/src/components/ForgotPasswordModal.vue b/src/components/ForgotPasswordModal.vue new file mode 100644 index 0000000..c29b3aa --- /dev/null +++ b/src/components/ForgotPasswordModal.vue @@ -0,0 +1,131 @@ + + + \ No newline at end of file diff --git a/src/locales/en.json b/src/locales/en.json index 851679e..f8e926f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,13 +1,19 @@ { + "Confirm Password": "Confirm Password", "Failed to fetch user-profile, please try again": "Failed to fetch user-profile, please try again", + "Failed to reset password, please try again and follow the instructions for creating a new password.": "Failed to reset password, please try again and follow the instructions for creating a new password.", + "Failed to send password reset link, please try again or contact administrator.": "Failed to send password reset link, please try again or contact administrator.", + "Finish resetting your password": "Finish resetting your password", "Launch Pad": "Launch Pad", "Login": "Login", "Logout": "Logout", "Logging out...": "Logging out...", + "New Password": "New Password", "Next": "Next", "Not configured": "Not configured", "OMS": "OMS", "Password": "Password", + "Reset Password":"Reset Password", "Please fill in the OMS": "Please fill in the OMS", "Please fill in the user details": "Please fill in the user details", "Processing": "Processing", @@ -15,5 +21,9 @@ "Sorry, your username or password is incorrect. Please try again.": "Sorry, your username or password is incorrect. Please try again.", "This application is not enabled for your account": "This application is not enabled for your account", "Username": "Username", - "View profile": "View profile" + "Username cannot be empty.": "Username cannot be empty.", + "View profile": "View profile", + "Your password has been successfully reset.": "Your password has been successfully reset.", + "Your request for reset password has been processed. Please check your email, for further instructions.":"Your request for reset password has been processed. Please check your email, for further instructions.", + "Your username must have an email already associated with it in HotWax to receive a reset password email. If you do not have an email linked to your account already, please contact your administrator to manually reset your password.":"Your username must have an email already associated with it in HotWax to receive a reset password email. If you do not have an email linked to your account already, please contact your administrator to manually reset your password." } \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 2a5fe6c..0d59fc0 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -3,6 +3,7 @@ import { RouteRecordRaw } from 'vue-router'; import Home from '@/views/Home.vue'; import Login from '@/views/Login.vue'; import { useAuthStore } from "@/store/auth"; +import ResetPassword from '@/views/ResetPassword.vue'; const loginGuard = (to: any, from: any, next: any) => { const authStore = useAuthStore() @@ -12,6 +13,23 @@ const loginGuard = (to: any, from: any, next: any) => { next(); }; +const authGuard = async (to: any, from: any, next: any) => { + const authStore = useAuthStore() + if (!authStore.isAuthenticated) { + next('/login') + } + next() +}; + +const resetGuard = async (to: any, from: any, next: any) => { + const authStore = useAuthStore() + if (authStore.requirePasswordChange) { + next('/resetPassword') + return; + } + next() +}; + const routes: Array = [ { path: '/', @@ -21,13 +39,20 @@ const routes: Array = [ path: '/home', name: 'Home', component: Home, + beforeEnter: resetGuard }, { path: '/login', name: 'Login', component: Login, beforeEnter: loginGuard - } + }, + { + path: '/resetPassword', + name: 'ResetPassword', + component: ResetPassword, + beforeEnter: authGuard + }, ]; const router = createRouter({ diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 837cf9d..f16eef1 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -130,9 +130,27 @@ const getUserPermissions = async (payload: any, token: any): Promise => { } } +const resetPassword = async(params: any) : Promise => { + return api({ + url: "service/resetPassword", + method: "POST", + data: params + }) +} + +const forgotPassword = async(params: any) : Promise => { + return api({ + url: "sendResetPasswordEmail", + method: "post", + data: params + }) +} + export const UserService = { getUserProfile, checkLoginOptions, login, - getUserPermissions + getUserPermissions, + resetPassword, + forgotPassword } \ No newline at end of file diff --git a/src/store/auth.ts b/src/store/auth.ts index 58a34fe..051619e 100644 --- a/src/store/auth.ts +++ b/src/store/auth.ts @@ -21,6 +21,7 @@ export const useAuthStore = defineStore('authStore', { expiration: undefined }, redirectUrl: '', + requirePasswordChange: false, // denotes if password change is required for the user maargOms: '', permissions: [] as any }), @@ -64,6 +65,8 @@ export const useAuthStore = defineStore('authStore', { expiration: resp.data.expirationTime } + this.requirePasswordChange = resp.data.requirePasswordChange + this.current = await UserService.getUserProfile(this.token.value); updateToken(this.token.value) diff --git a/src/views/Login.vue b/src/views/Login.vue index f34cd45..1e8fb97 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -41,6 +41,13 @@ + + + + {{ $t('The username or password you entered is incorrect. Please try again.') }} + {{ $t('forgot password') }} + + @@ -67,18 +74,20 @@ import { IonItem, IonPage, IonSpinner, - loadingController + loadingController, + modalController } from "@ionic/vue"; import { defineComponent } from "vue"; import { useRouter } from "vue-router"; import { useAuthStore } from "@/store/auth"; import Logo from '@/components/Logo.vue'; -import { arrowForwardOutline, gridOutline } from 'ionicons/icons' +import { arrowForwardOutline, gridOutline,warningOutline } from 'ionicons/icons' import { UserService } from "@/services/UserService"; import { translate } from "@/i18n"; import { appInfo, isMaargLogin, isOmsWithMaarg, showToast } from "@/util"; import { hasError } from "@hotwax/oms-api"; import { Actions, hasPermission } from "@/authorization"; +import ForgotPasswordModal from "@/components/ForgotPasswordModal.vue"; export default defineComponent({ name: "Login", @@ -109,12 +118,16 @@ export default defineComponent({ loader: null as any, loginOption: {} as any, isCheckingOms: false, - isLoggingIn: false + isLoggingIn: false, + errorMessage: '', }; }, ionViewWillEnter() { this.initialise() }, + ionViewWillLeave() { + this.errorMessage = '' + }, methods: { async initialise() { this.hideBackground = true @@ -256,6 +269,14 @@ export default defineComponent({ this.isLoggingIn = true; try { await this.authStore.login(username.trim(), password) + // when password needs to be changed, redirecting the user to reset page + if(this.authStore.requirePasswordChange) { + this.username = '' + this.password = '' + this.router.push('/resetPassword'); + this.isLoggingIn = false + return + } if (this.authStore.getRedirectUrl) { this.generateRedirectionLink() } else { @@ -264,7 +285,8 @@ export default defineComponent({ this.password = '' this.router.push('/') } - } catch (error) { + } catch (error:any) { + this.errorMessage = error console.error(error) } this.isLoggingIn = false; @@ -339,6 +361,16 @@ export default defineComponent({ omsUrl = omsUrl ? omsUrl : this.authStore.oms.startsWith('http') ? this.authStore.oms.includes('/api') ? this.authStore.oms : `${this.authStore.oms}/api/` : this.authStore.oms window.location.replace(`${url}?oms=${omsUrl}&token=${this.authStore.token.value}&expirationTime=${this.authStore.token.expiration}${omsRedirectionUrl ? '&omsRedirectionUrl=' + omsRedirectionUrl : ''}`) + }, + async openForgotPasswordModal(){ + const forgotPasswordModal = await modalController.create({ + component:ForgotPasswordModal, + }) + forgotPasswordModal.onDidDismiss() + this.errorMessage = '' + this.username = '' + this.password = '' + return forgotPasswordModal.present() } }, setup () { @@ -348,7 +380,8 @@ export default defineComponent({ arrowForwardOutline, authStore, gridOutline, - router + router, + warningOutline }; } }); @@ -364,5 +397,12 @@ export default defineComponent({ align-items: center; height: 100%; } - +.ion-item-banner { + --background:#ED576B1A; + --border-radius: 8px; +} +.ion-item-banner a { + display: block; + color: var(--ion-color-danger) ; +} diff --git a/src/views/ResetPassword.vue b/src/views/ResetPassword.vue new file mode 100644 index 0000000..d15d625 --- /dev/null +++ b/src/views/ResetPassword.vue @@ -0,0 +1,156 @@ + + + + + \ No newline at end of file