Skip to content

Commit

Permalink
WIP front end permission sets
Browse files Browse the repository at this point in the history
  • Loading branch information
kyle1morel committed Jul 23, 2024
1 parent 079f4f9 commit 01e059d
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 10 deletions.
1 change: 1 addition & 0 deletions app/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as roadmapController } from './roadmap';
export { default as ssoController } from './sso';
export { default as submissionController } from './submission';
export { default as userController } from './user';
export { default as yarsController } from './yars';
22 changes: 22 additions & 0 deletions app/src/controllers/yars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { yarsService } from '../services';

import type { NextFunction, Request, Response } from '../interfaces/IExpress';

const controller = {
getPermissions: async (req: Request, res: Response, next: NextFunction) => {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const roles = await yarsService.getIdentityRoles((req.currentUser?.tokenPayload as any).preferred_username);

const permissions = await Promise.all(roles.map((x) => yarsService.getRolePermissions(x.roleId))).then((x) =>
x.flat()
);

res.status(200).json(permissions);
} catch (e: unknown) {
next(e);
}
}
};

export default controller;
12 changes: 10 additions & 2 deletions app/src/middleware/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@ export const hasPermission = (resource: string, action: string) => {
try {
if (req.currentUser) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const roles = await yarsService.getIdentityRoles((req.currentUser?.tokenPayload as any).preferred_username);
const identityId = (req.currentUser?.tokenPayload as any).preferred_username;

let roles = await yarsService.getIdentityRoles(identityId);

// Auto assign PROPONENT if user has no roles
if (roles && roles.length === 0) {
await yarsService.assignRole(identityId, AccessRole.PROPONENT);
roles = await yarsService.getIdentityRoles(identityId);
}

const userId = await userService.getCurrentUserId(getCurrentIdentity(req.currentUser, NIL), NIL);

if (!userId) {
throw new Error('Invalid role user');
throw new Error('Invalid user');
}

// Permission/Scope checking for non developers
Expand Down
5 changes: 4 additions & 1 deletion app/src/routes/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import roadmap from './roadmap';
import sso from './sso';
import submission from './submission';
import user from './user';
import yars from './yars';

import { Initiative } from '../../utils/enums/application';

const router = express.Router();
Expand All @@ -20,7 +22,7 @@ router.use(setInitiative(Initiative.HOUSING));
// Base v1 Responder
router.get('/', (_req, res) => {
res.status(200).json({
endpoints: ['/document', '/enquiry', '/note', '/permit', '/roadmap', '/sso', '/submission', '/user']
endpoints: ['/document', '/enquiry', '/note', '/permit', '/roadmap', '/sso', '/submission', '/user', '/yars']
});
});

Expand All @@ -32,5 +34,6 @@ router.use('/roadmap', roadmap);
router.use('/sso', sso);
router.use('/submission', submission);
router.use('/user', user);
router.use('/yars', yars);

export default router;
15 changes: 15 additions & 0 deletions app/src/routes/v1/yars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import express from 'express';

import { yarsController } from '../../controllers';
import { requireSomeAuth } from '../../middleware/requireSomeAuth';

import type { NextFunction, Request, Response } from '../../interfaces/IExpress';

const router = express.Router();
router.use(requireSomeAuth);

router.get('/permissions', (req: Request, res: Response, next: NextFunction): void => {
yarsController.getPermissions(req, res, next);
});

export default router;
59 changes: 58 additions & 1 deletion app/src/services/yars.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
/* eslint-disable no-useless-catch */

import prisma from '../db/dataConnection';
import { Initiative } from '../utils/enums/application';
import { AccessRole, Initiative } from '../utils/enums/application';

const service = {
assignRole: async (identityId: string, role: AccessRole, initiative?: Initiative) => {
try {
let roleResult;

if (initiative) {
const i = await prisma.initiative.findFirstOrThrow({
where: {
code: initiative
}
});

roleResult = await prisma.role.findFirstOrThrow({
where: {
initiative_id: i.initiative_id,
user_type: role
}
});
} else {
roleResult = await prisma.role.findFirstOrThrow({
where: {
initiative_id: null,
user_type: role
}
});
}

const result = await prisma.identity_role.create({
data: {
identity_id: identityId,
role_id: roleResult.role_id
}
});

return { identityId: result.identity_id, roleId: result.role_id };
} catch (e: unknown) {
throw e;
}
},

/**
* @function getEnquiry
* Gets roles for the specified identity
Expand Down Expand Up @@ -55,6 +94,24 @@ const service = {
} catch (e: unknown) {
throw e;
}
},

getRolePermissions: async (roleId: number) => {
try {
const result = await prisma.role_permission_vw.findMany({
where: {
role_id: roleId
}
});

return result.map((x) => ({
initiativeName: x.initiative_name,
resourceName: x.resource_name,
actionName: x.action_name
}));
} catch (e: unknown) {
throw e;
}
}
};

Expand Down
1 change: 1 addition & 0 deletions frontend/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export { default as permitService } from './permitService';
export { default as roadmapService } from './roadmapService';
export { default as submissionService } from './submissionService';
export { default as userService } from './userService';
export { default as yarsService } from './yarsService';
11 changes: 11 additions & 0 deletions frontend/src/services/yarsService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { appAxios } from './interceptors';

import type { AxiosResponse } from 'axios';

const PATH = 'yars';

export default {
getPermissions(): Promise<AxiosResponse> {
return appAxios().get(`${PATH}/permissions`);
}
};
1 change: 1 addition & 0 deletions frontend/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export { default as useAppStore } from './appStore';
export { default as useAuthStore } from './authStore';
export { default as useConfigStore } from './configStore';
export { default as useEnquiryStore } from './enquiryStore';
export { default as usePermissionStore } from './permissionStore';
export { default as useSubmissionStore } from './submissionStore';
export { default as useTypeStore } from './typeStore';
43 changes: 43 additions & 0 deletions frontend/src/store/permissionStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { defineStore } from 'pinia';
import { computed, readonly, ref } from 'vue';

import type { Ref } from 'vue';

export type PermissionStoreState = {
permissions: Ref<Array<any>>;
};

export const usePermissionStore = defineStore('permission', () => {
// State
const state: PermissionStoreState = {
permissions: ref([])
};

// Getters
const getters = {
can: computed(
() => (initiative: string, resource: string, action: string) =>
state.permissions.value.find(
(x) => x.initiative === initiative && x.resource === resource && x.action === action
)
)
};

// Actions
function setPermissions(data: any) {
state.permissions.value = data;
}

return {
// State
state: readonly(state),

// Getters
...getters,

// Actions
setPermissions
};
});

export default usePermissionStore;
12 changes: 6 additions & 6 deletions frontend/src/views/oidc/OidcCallbackView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { Spinner } from '@/components/layout';
import { PermissionService } from '@/services';
import { useAuthStore } from '@/store';
import { yarsService } from '@/services';
import { useAuthStore, usePermissionStore } from '@/store';
import { StorageKey } from '@/utils/enums/application';
const authStore = useAuthStore();
Expand All @@ -13,10 +13,10 @@ const router = useRouter();
onMounted(async () => {
await authStore.loginCallback();
// Request basic access if the logged in user has no roles
if (!authStore.getClientRoles || !authStore.getClientRoles.length) {
await new PermissionService().requestBasicAccess();
}
// Get front end permissions
const permissions = await yarsService.getPermissions();
console.log(permissions);

Check warning on line 18 in frontend/src/views/oidc/OidcCallbackView.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 18 in frontend/src/views/oidc/OidcCallbackView.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 18 in frontend/src/views/oidc/OidcCallbackView.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement
usePermissionStore().setPermissions(permissions.data);
// Return user back to original login entrypoint if specified
const entrypoint = window.sessionStorage.getItem(StorageKey.AUTH);
Expand Down

0 comments on commit 01e059d

Please sign in to comment.