Skip to content

Commit

Permalink
Add beforeEnter accessHandler
Browse files Browse the repository at this point in the history
Checks user access to a route and redirects if necessary
  • Loading branch information
kyle1morel committed Sep 18, 2024
1 parent 6a6649b commit 0827fc0
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 49 deletions.
106 changes: 61 additions & 45 deletions frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,45 @@ import { useAppStore, useAuthNStore, useAuthZStore } from '@/store';
import { NavigationPermission } from '@/store/authzStore';
import { RouteName, StorageKey } from '@/utils/enums/application';

import type { RouteRecordRaw } from 'vue-router';
import type { RouteLocationNormalizedGeneric, RouteRecordRaw } from 'vue-router';

/**
* @function accessHandler
* Checks for user access to the requested route and redirect if necessary
* @param {object} to The route to navigate to
* @returns {object} a Vue route
*/
function accessHandler(to: RouteLocationNormalizedGeneric) {
const access = to.meta.access as NavigationPermission[];

if (access && access.length) {
const authzStore = useAuthZStore();
if (!authzStore.canNavigate(access)) {
return { name: RouteName.NOT_FOUND };
}
}
}

/**
* @function bootstrap
* Obtains user permissions if authenticated before hard navigation
* @returns {object} a Vue route
*/
async function bootstrap() {
const authnStore = useAuthNStore();
const authzStore = useAuthZStore();

const { getIsAuthenticated } = storeToRefs(authnStore);

if (getIsAuthenticated.value && !authzStore.getGroups.length) {
const permissions = await yarsService.getPermissions();
authzStore.setPermissions(permissions.data);
}
}

/**
* @function createProps
* Parses the route query and params to generate vue props
* Parses the route query and params to generate Vue props
* @param {object} route The route object
* @returns {object} a Vue props object
*/
Expand All @@ -22,40 +56,19 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: () => import('@/views/GenericView.vue'),
beforeEnter: async (to) => {
// Get user groups if we haven't before any routing
const authnStore = useAuthNStore();
const { getIsAuthenticated } = storeToRefs(authnStore);
const authzStore = useAuthZStore();
if (getIsAuthenticated.value && !authzStore.getGroups.length) {
const permissions = await yarsService.getPermissions();
authzStore.setPermissions(permissions.data);
}

// Check access
if (to.meta.access) {
if (!authzStore.canNavigate(Array.isArray(to.meta.access) ? to.meta.access : [to.meta.access])) {
useRouter().replace({ name: RouteName.NOT_FOUND });
return;
}
}
},
beforeEnter: [bootstrap, accessHandler],
children: [
{
path: '/',
name: RouteName.HOME,
component: () => import('@/views/HomeView.vue')
},
{
path: '/soon',
name: RouteName.COMING_SOON,
component: () => import('@/views/ComingSoon.vue')
},
{
path: '/developer',
name: RouteName.DEVELOPER,
component: () => import('@/views/DeveloperView.vue'),
meta: { requiresAuth: true, access: NavigationPermission.DEVELOPER }
beforeEnter: accessHandler,
meta: { requiresAuth: true, access: [NavigationPermission.DEVELOPER] }
},
{
path: '/forbidden',
Expand All @@ -64,16 +77,20 @@ const routes: Array<RouteRecordRaw> = [
},
{
path: '/housing',
component: () => import('@/views/GenericView.vue'),

meta: { requiresAuth: true },
children: [
{
path: '',
name: RouteName.HOUSING,
component: () => import('../views/housing/HousingView.vue'),
beforeEnter: () => {
const authzStore = useAuthZStore();
if (!authzStore.canNavigate(NavigationPermission.HOUSING)) {
useRouter().replace({ name: RouteName.HOUSING_SUBMISSIONS });
}
},
meta: {
access: NavigationPermission.HOUSING
access: [NavigationPermission.HOUSING]
}
},
{
Expand All @@ -83,18 +100,20 @@ const routes: Array<RouteRecordRaw> = [
path: '',
name: RouteName.HOUSING_ENQUIRY,
component: () => import('../views/housing/enquiry/EnquiryView.vue'),
beforeEnter: accessHandler,
props: createProps,
meta: {
access: NavigationPermission.HOUSING_ENQUIRY
access: [NavigationPermission.HOUSING_ENQUIRY]
}
},
{
path: 'intake',
name: RouteName.HOUSING_ENQUIRY_INTAKE,
component: () => import('@/views/housing/enquiry/EnquiryIntakeView.vue'),
beforeEnter: accessHandler,
props: createProps,
meta: {
access: NavigationPermission.HOUSING_INTAKE
access: [NavigationPermission.HOUSING_INTAKE]
}
}
]
Expand All @@ -106,18 +125,20 @@ const routes: Array<RouteRecordRaw> = [
path: '',
name: RouteName.HOUSING_SUBMISSION,
component: () => import('@/views/housing/submission/SubmissionView.vue'),
beforeEnter: accessHandler,
props: createProps,
meta: {
access: NavigationPermission.HOUSING_SUBMISSION
access: [NavigationPermission.HOUSING_SUBMISSION]
}
},
{
path: 'intake',
name: RouteName.HOUSING_SUBMISSION_INTAKE,
component: () => import('@/views/housing/submission/SubmissionIntakeView.vue'),
beforeEnter: accessHandler,
props: createProps,
meta: {
access: NavigationPermission.HOUSING_INTAKE
access: [NavigationPermission.HOUSING_INTAKE]
}
}
]
Expand All @@ -131,6 +152,7 @@ const routes: Array<RouteRecordRaw> = [
{
path: '',
component: () => import('@/views/housing/project/ProjectListView.vue'),
beforeEnter: accessHandler,
meta: {
access: [NavigationPermission.HOUSING_STATUS_TRACKER]
},
Expand All @@ -139,6 +161,7 @@ const routes: Array<RouteRecordRaw> = [
{
path: ':submissionId',
component: () => import('@/views/housing/project/ProjectView.vue'),
beforeEnter: accessHandler,
meta: {
access: [NavigationPermission.HOUSING_STATUS_TRACKER]
},
Expand All @@ -151,6 +174,7 @@ const routes: Array<RouteRecordRaw> = [
path: 'submissions',
name: RouteName.HOUSING_SUBMISSIONS,
component: () => import('@/views/housing/SubmissionsView.vue'),
beforeEnter: accessHandler,
meta: {
access: [NavigationPermission.HOUSING_SUBMISSIONS, NavigationPermission.HOUSING_SUBMISSIONS_SUB]
}
Expand All @@ -159,13 +183,12 @@ const routes: Array<RouteRecordRaw> = [
path: '/guide',
name: RouteName.HOUSING_GUIDE,
component: () => import('@/views/ComingSoon.vue'),
meta: { access: NavigationPermission.HOUSING }
meta: { access: [NavigationPermission.HOUSING] }
}
]
},
{
path: '/oidc',
component: () => import('@/views/GenericView.vue'),
children: [
{
path: 'callback',
Expand All @@ -190,7 +213,9 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/user',
name: RouteName.USER_MANAGEMENT,
component: () => import('@/views/user/UserManagementView.vue')
component: () => import('@/views/user/UserManagementView.vue'),
beforeEnter: accessHandler,
meta: { requiresAuth: true, access: [NavigationPermission.HOUSING_USER_MANAGEMENT] }
},
{
path: '/:pathMatch(.*)*',
Expand All @@ -204,7 +229,6 @@ const routes: Array<RouteRecordRaw> = [
export default function getRouter() {
const appStore = useAppStore();
const authService = new AuthService();
const authzStore = useAuthZStore();

const router = createRouter({
history: createWebHistory(),
Expand Down Expand Up @@ -245,14 +269,6 @@ export default function getRouter() {
return;
}
}

// Check for reroutes
if (to.name === RouteName.HOUSING) {
if (!authzStore.canNavigate(NavigationPermission.HOUSING)) {
router.replace({ name: RouteName.HOUSING_SUBMISSIONS });
return;
}
}
});

router.afterEach(() => {
Expand Down
1 change: 0 additions & 1 deletion frontend/src/utils/enums/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export enum Regex {
}

export enum RouteName {
COMING_SOON = 'coming_soon',
DEVELOPER = 'developer',
FORBIDDEN = 'forbidden',
HOME = 'home',
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/views/user/UserManagementView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ function assignUserStatus(request: UserAccessRequest) {
return request;
}
function onDenyRevocation(user: UserAccessRequest) {
//function onDenyRevocation(user: UserAccessRequest) {
function onDenyRevocation() {
confirm.require({
message: 'This user’s revocation request will be denied, and they will remain an authorized user.',
header: 'Deny revocation request',
Expand All @@ -73,9 +74,8 @@ function onDenyRevocation(user: UserAccessRequest) {
rejectLabel: 'Cancel',
accept: async () => {
try {
console.log('TODO');
// TODO
// const response = await accessRequestService.denyAccessRequest(user.accessRequest?.accessRequestId as string);
// if (response) {
// usersAndAccessRequests.value = usersAndAccessRequests.value.filter(
// (userAccessRequest) => userAccessRequest.user.userId !== response.data.userId
Expand Down

0 comments on commit 0827fc0

Please sign in to comment.