/**
* @module router-middlewares
* @category Router
*/
import Vue from 'vue'
import store from '../store'
import NProgress from 'nprogress'
import { getRouteQuery, getQueryStringValue } from '@/utils/querystring'
import {
getFirstGrantedRouteFromVueRoutes,
isVueRouteGranted,
} from '@/services/rights-service.js'
const shouldLog =
(getQueryStringValue('verbose') || '').includes('1') ||
(getQueryStringValue('verbose') || '').includes('router')
const isAuthEnabled = true //Disable for tests
NProgress.configure({ showSpinner: false })
var router
function hasAlertMessages() {
return store.getters['alert/alerts'].length !== 0
}
export function configureMiddlewares(_router) {
router = _router
router.beforeEach(beforeEach)
router.afterEach(afterEach)
}
/**
* If user cannot access a route (401), we will redirect him to the first granted route
*/
function getFirstGrantedRoute() {
return getFirstGrantedRouteFromVueRoutes(router.options.routes)
}
/**
*
* If login -> if alerts -> proceed to login
* If login -> if requires auth -> proceed to login
* If login -> if no alerts/require auth, try autologin
* If no login -> *require auth
*
* *require auth:
* redirect to the login screen if not logged
*/
async function beforeEach(to, from, next) {
NProgress.start()
NProgress.set(0.1)
/*
if (from.meta.functionId) {
Vue.$loggingService.activityAuditManager.stopActivityAudit(
to.meta.functionId
);
}*/
//Todo: Stop API requests from previous route
//Do not bring a loder from previous route
try {
Vue.$loader.hide()
} catch (err) {
console.warn('Loader fail to show') //Might fail if unit-test
}
//Wait DOM update (To switch layout)
Vue.nextTick(() => {
if (!isAuthEnabled) {
return next()
}
if (to.name === 'login_screen') {
//If alerts messages present or requires auth, proceed to login screen
if (hasAlertMessages() || to.params.requiresAuth) {
return next()
}
//On first page load, try auto-login with stored JWT
return autoLogin(to, from, next)
} else {
//Normal routes check for auth...
return requireAuth(to, from, next)
}
})
}
function afterEach(to) {
setTimeout(() => NProgress.done(), 500)
}
function getTokenFromRoute(route) {
if (route && route.query && route.query._token) {
return route.query._token
} else {
return getQueryStringValue('_token')
}
}
function hasEnoughRights(to) {
if (!(to.meta && to.meta.requiredRights)) {
return true
}
return (
to.meta.requiredRights
.map((rightCode) => {
return Vue.$rights.hasRight(rightCode) ? '1' : '0'
})
.join('')
.indexOf('0') === -1
)
}
function routeToString(r) {
return {
name: r.name,
query: r.query,
meta: r.meta,
}
}
/**
* Vue router middleware (auth guard)
* Will redirect to the login screen if not logged
*
* @param {*} to
* @param {*} from
* @param {*} next
*/
async function requireAuth(to, from, next) {
shouldLog &&
Vue.$log.debug('requireAuth', {
from: routeToString(from),
to: routeToString(to),
})
if (from.name === null) {
await store.dispatch('auth/syncLogged', {
token: getTokenFromRoute(to.query._token) || null,
})
}
if (!store.getters['auth/isLogged']) {
shouldLog &&
Vue.$log.debug('requireAuth::intercepted', {
from: routeToString(from),
to: routeToString(to),
})
return router.push({
name: 'login_screen',
params: {
requiresAuth: true,
redirectAfterAutoLoginRoute: {
...to,
},
},
})
}
handleNext(to, from, next)
}
async function handleNext(to, from, next) {
//Case: User has no enough rights to target module
if (!hasEnoughRights(to)) {
let existingRoutes = router.options.routes
.map((r) => ({
name: r.name,
meta: r.meta || {},
}))
.filter((r) => !['login_screen'].includes(r.name))
to = {
hasChange: true,
}
//Case: User is redirected to other accessible module
if (store.getters['auth/rightsList'].length > 0) {
let firstAccessibleModuleRoute = getFirstGrantedRoute()
if (firstAccessibleModuleRoute) {
shouldLog &&
Vue.$log.debug(
'not enough rights, redirecting to first granted module:',
firstAccessibleModuleRoute.name
)
to.name = firstAccessibleModuleRoute.name
}
}
//Case: User as no rights to modules
if (!to.name) {
shouldLog && Vue.$log.debug('not enough rights, redirecting to login')
to.name = 'login_screen'
await store.dispatch('auth/logout')
store.dispatch(
'alert/addAlert',
{
title: '403',
text: 'alerts.NOT_ENOUGH_RIGHTS',
type: 'warning',
},
{
root: true,
}
)
}
}
if (to.meta && to.meta.functionId) {
Vue.$loggingService.activityAuditManager.startActivityAudit(
to.meta.functionId
)
}
if (to.hasChange) {
shouldLog && Vue.$log.debug('handling next (has change)')
return router.push(to)
}
if (from.query.s && !to.query.s) {
if (to.path === from.path) {
return // This is a no-no via the documentation, but a bug in routing to identical routes strips query params, and this prevents that
}
next({
name: to.name,
query: {
s: from.query.s,
},
})
} else {
next()
}
}
/**
*
* Vue router middleware
* Will try to autologin using the stored credentials (JWT token)
*
* @param {*} to
* @param {*} from
* @param {*} next
*/
async function autoLogin(to, from, next) {
shouldLog && Vue.$log.debug('autoLogin')
//Case: User is not logged
if (!store.getters['auth/isLogged']) {
let nextRoute =
(to.params.redirectAfterAutoLoginRoute && {
...to.params.redirectAfterAutoLoginRoute,
hasChange: true,
}) ||
null
let nextRouteQuery = getRouteQuery(nextRoute)
shouldLog &&
Vue.$log.debug('autoLogin', {
nextRoute,
nextRouteQuery,
})
await store.dispatch('auth/syncLogged', {
token: getTokenFromRoute(nextRoute) || null,
})
//Case: User autologin success
if (store.getters['auth/isLogged']) {
//Case: Try to redirect to next route
if (nextRoute) {
shouldLog &&
console.info(
`AUTOLOGIN redirecting to next route ${nextRoute.name}`,
{
nextRoute,
}
)
return handleNext(nextRoute, from, next)
} else {
Vue.$routerPlugin.routeToDefaultRoute()
}
}
} else {
shouldLog && Vue.$log.debug('autoLogin fails')
}
next()
}
Source