From e5c2e4d2552311cce19e48c7090b9b91ec0c9aff Mon Sep 17 00:00:00 2001 From: Oyinlola Olasunkanmi Raymond <60177090+olasunkanmi-SE@users.noreply.github.com> Date: Fri, 16 Feb 2024 07:53:09 +0800 Subject: [PATCH 1/3] update the authorization technique to check if user role matches ite priority (#488) Co-authored-by: Olasunkanmi Oyinlola --- backend/src/application/constants/constants.ts | 6 ++++-- .../repositories/schemas/singleclient.schema.ts | 4 ++-- .../src/shared/services/access_control.service.ts | 13 +++++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/backend/src/application/constants/constants.ts b/backend/src/application/constants/constants.ts index 2acba4d7..406d78e6 100644 --- a/backend/src/application/constants/constants.ts +++ b/backend/src/application/constants/constants.ts @@ -23,13 +23,15 @@ export const tokenExpiresIn = 3600000; export enum Role { ADMIN = 'ADMIN', USER = 'USER', - GUEST = 'CLIENT', + CLIENT = 'CLIENT', + SUPERADMIN = 'SUPERADMIN', } export const RoleOrder: Record = { - [Role.GUEST]: 1, + [Role.CLIENT]: 1, [Role.USER]: 2, [Role.ADMIN]: 3, + [Role.SUPERADMIN]: 4, }; export const ROLE_KEY = 'role'; diff --git a/backend/src/infrastructure/data_access/repositories/schemas/singleclient.schema.ts b/backend/src/infrastructure/data_access/repositories/schemas/singleclient.schema.ts index 6f8967e1..dcf5e214 100644 --- a/backend/src/infrastructure/data_access/repositories/schemas/singleclient.schema.ts +++ b/backend/src/infrastructure/data_access/repositories/schemas/singleclient.schema.ts @@ -2,7 +2,7 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; import { BaseDocument } from '../../../database'; import { ISingleClientData } from '../models/singleclient-model.interface'; -import { SingleClientStatus } from '../../../../application/constants/constants'; +import { Role, SingleClientStatus } from '../../../../application/constants/constants'; export type SingleClientDocument = SingleClientDataModel & Document; @@ -29,7 +29,7 @@ export class SingleClientDataModel extends BaseDocument implements ISingleClient @Prop({ type: String, required: true }) passwordHash: string; - @Prop({ type: String, default: 'admin' }) + @Prop({ type: String, enum: Object.values(Role), default: Role.USER }) role: string; @Prop({ type: Boolean, default: false }) diff --git a/backend/src/shared/services/access_control.service.ts b/backend/src/shared/services/access_control.service.ts index 5faa9490..6a38e82a 100644 --- a/backend/src/shared/services/access_control.service.ts +++ b/backend/src/shared/services/access_control.service.ts @@ -27,14 +27,19 @@ export class AccessControlService implements IAccessControlService { } public isAuthorized({ currentRole, requiredRole }: IIsAuthorizedProps): boolean { - let authorized = false; + let isAuthorized = false; for (const hierarchy of this.hierarchies) { const priority = hierarchy.get(currentRole); const requirePriority = hierarchy.get(requiredRole); - if (priority && requirePriority && priority >= requirePriority) { - authorized = true; + if (priority && requirePriority) { + if (priority >= requirePriority) { + isAuthorized = true; + } + if (priority === requirePriority) { + isAuthorized = true; + } } } - return authorized; + return isAuthorized; } } From 18179d313f0ee3fc388c79254cb5a759bb1a3792 Mon Sep 17 00:00:00 2001 From: Olasunkanmi Oyinlola Date: Fri, 16 Feb 2024 08:25:05 +0800 Subject: [PATCH 2/3] include roles as a sign up property, add the history menu to the landing page --- .../infrastructure/filters/exception.filter.ts | 4 ++-- .../dtos/create-singleclient.dto.ts | 5 +++++ .../src/singleclient/singleclient.service.ts | 2 +- frontend/src/components/Utilities/Navbar.tsx | 17 +++++++++++++---- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/backend/src/infrastructure/filters/exception.filter.ts b/backend/src/infrastructure/filters/exception.filter.ts index af07a0c3..c953b61a 100644 --- a/backend/src/infrastructure/filters/exception.filter.ts +++ b/backend/src/infrastructure/filters/exception.filter.ts @@ -1,11 +1,11 @@ -import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus, Inject } from '@nestjs/common'; +import { ArgumentsHost, Catch, HttpException, HttpStatus, Inject } from '@nestjs/common'; +import { BaseExceptionFilter } from '@nestjs/core'; import { Request } from 'express'; import * as fs from 'fs'; import { TYPES } from '../../application/constants'; import { IContextAwareLogger } from '../logger'; import { APIResponseMessage } from './../../application/constants/constants'; import { IExceptionResponse, IRequestException } from './exception-response.interface'; -import { BaseExceptionFilter } from '@nestjs/core'; @Catch() export class ApplicationExceptionsFilter extends BaseExceptionFilter { diff --git a/backend/src/singleclient/dtos/create-singleclient.dto.ts b/backend/src/singleclient/dtos/create-singleclient.dto.ts index b67b897e..ba824938 100644 --- a/backend/src/singleclient/dtos/create-singleclient.dto.ts +++ b/backend/src/singleclient/dtos/create-singleclient.dto.ts @@ -10,4 +10,9 @@ export class CreateSingleClientDTO { @IsNotEmpty() @MaxLength(256) readonly passwordHash: string; + + @IsString() + @IsNotEmpty() + @MaxLength(128) + readonly role: string; } diff --git a/backend/src/singleclient/singleclient.service.ts b/backend/src/singleclient/singleclient.service.ts index 2d3bccc2..23f109b6 100644 --- a/backend/src/singleclient/singleclient.service.ts +++ b/backend/src/singleclient/singleclient.service.ts @@ -48,7 +48,7 @@ export class SingleClientService extends AuthService implements ISingleClientSer const { email } = props; const existingSingleClient: Result = await this.singleclientRepository.findOne({ email }); if (existingSingleClient.isSuccess && existingSingleClient.getValue().email === email) { - throwApplicationError(HttpStatus.BAD_REQUEST, `User with email ${props.email} already exists`); + throwApplicationError(HttpStatus.BAD_REQUEST, `User already exists, please sign in.`); } const audit: Audit = Audit.createInsertContext(context); diff --git a/frontend/src/components/Utilities/Navbar.tsx b/frontend/src/components/Utilities/Navbar.tsx index eb78b905..467fd13c 100644 --- a/frontend/src/components/Utilities/Navbar.tsx +++ b/frontend/src/components/Utilities/Navbar.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import { Button, Nav, Stack } from "react-bootstrap"; import Container from "react-bootstrap/Container"; import Navbar from "react-bootstrap/Navbar"; -import { NavLink, Outlet, useNavigate } from "react-router-dom"; +import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom"; import { CONSTANTS } from "../../constants/constant"; import { useShoppingCart } from "../../hooks/UseShoppingCart"; import { calculateQuantity } from "../../utility/utils"; @@ -16,6 +16,7 @@ export const Navigation = () => { const handleCloseModal = () => setShowModal(false); const handleShowModal = () => setShowModal(true); const navigate = useNavigate(); + const location = useLocation(); const displayModal = () => { handleShowModal(); @@ -26,6 +27,16 @@ export const Navigation = () => { resetMenu(); }; + const handleNavMenu = () => { + return location.pathname === "/" ? ( + history + ) : ( + + Back + + ); + }; + return ( <> @@ -33,9 +44,7 @@ export const Navigation = () => { From a07aaad9ce178aec8d74899c085bff1938a7be4d Mon Sep 17 00:00:00 2001 From: Olasunkanmi Oyinlola Date: Fri, 16 Feb 2024 08:36:53 +0800 Subject: [PATCH 3/3] fix build errors --- backend/.vscode/launch.json | 2 +- backend/src/singleclient/singleclient-service.spec.ts | 4 +++- backend/src/singleclient/singleclient.service.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/.vscode/launch.json b/backend/.vscode/launch.json index 5a68d98e..c4066caa 100644 --- a/backend/.vscode/launch.json +++ b/backend/.vscode/launch.json @@ -21,7 +21,7 @@ "request": "launch", "name": "Test", "runtimeExecutable": "npm", - "runtimeArgs": ["run", "test:unit", "src/order/order.service.spec.ts", "--inspect-brk"], + "runtimeArgs": ["run", "test:unit", "src/singleclient/singleclient-service.spec.ts", "--inspect-brk"], // "src/infrastructure/data_access/db_repositories/worker_sql_repository.spec.ts", "console": "integratedTerminal", "restart": true, diff --git a/backend/src/singleclient/singleclient-service.spec.ts b/backend/src/singleclient/singleclient-service.spec.ts index c94bb04b..07dd9cdf 100644 --- a/backend/src/singleclient/singleclient-service.spec.ts +++ b/backend/src/singleclient/singleclient-service.spec.ts @@ -44,6 +44,7 @@ describe('Test singleclient service', () => { const createSingleClientProps = { email: 'ola@tesla.com', passwordHash: '', + role: 'USER', }; validateUserStub.getUser = async (): Promise => { return singleclientMockData; @@ -60,7 +61,7 @@ describe('Test singleclient service', () => { await singleclientService.createSingleClient(createSingleClientProps); } catch (error: any) { expect(error.status).to.eq(400); - expect(error.response.error).to.eq('User with email ola@tesla.com already exists'); + expect(error.response.error).to.eq('User already exists, sign in.'); } }); @@ -68,6 +69,7 @@ describe('Test singleclient service', () => { const createSingleClientProps = { email: 'ola@ola.com', passwordHash: '', + role: 'USER', }; contextServiceStub.getContext = (): Context => { return new Context(createSingleClientProps.email, ''); diff --git a/backend/src/singleclient/singleclient.service.ts b/backend/src/singleclient/singleclient.service.ts index 23f109b6..828e2274 100644 --- a/backend/src/singleclient/singleclient.service.ts +++ b/backend/src/singleclient/singleclient.service.ts @@ -48,7 +48,7 @@ export class SingleClientService extends AuthService implements ISingleClientSer const { email } = props; const existingSingleClient: Result = await this.singleclientRepository.findOne({ email }); if (existingSingleClient.isSuccess && existingSingleClient.getValue().email === email) { - throwApplicationError(HttpStatus.BAD_REQUEST, `User already exists, please sign in.`); + throwApplicationError(HttpStatus.BAD_REQUEST, `User already exists, sign in.`); } const audit: Audit = Audit.createInsertContext(context);