From 4bcb548e846d11c2a275cc6292b72e362ea71539 Mon Sep 17 00:00:00 2001 From: prooflesben Date: Sun, 7 Dec 2025 18:39:25 -0500 Subject: [PATCH 1/3] Added guards for routes and make it so that the back to login button on the restrictged page logs out the user --- backend/package-lock.json | 62 +++++++++++++++- backend/package.json | 3 + backend/src/auth/auth.controller.ts | 22 ++++-- backend/src/auth/auth.guard.ts | 71 +++++++++++++++++++ backend/src/auth/auth.service.ts | 4 +- backend/src/grant/grant.controller.ts | 4 +- backend/src/main.ts | 3 +- .../main-page/restricted/RestrictedPage.tsx | 5 ++ 8 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 backend/src/auth/auth.guard.ts diff --git a/backend/package-lock.json b/backend/package-lock.json index d5cfcf1..1cd896a 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,10 +13,12 @@ "@nestjs/jwt": "^8.0.0", "@nestjs/passport": "^8.0.0", "@nestjs/platform-express": "^8.4.7", + "aws-jwt-verify": "^5.1.1", "aws-sdk": "^2.1030.0", "aws-serverless-express": "^3.4.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "cookie-parser": "^1.4.7", "crypto": "^1.0.1", "dotenv": "^16.4.5", "jwt-decode": "^4.0.0", @@ -29,6 +31,7 @@ "@nestjs/testing": "^8.4.7", "@types/aws-lambda": "^8.10.149", "@types/aws-serverless-express": "^3.3.10", + "@types/cookie-parser": "^1.4.10", "@types/jest": "^27.0.0", "@types/node": "^20.0.0", "esbuild": "^0.25.2", @@ -83,6 +86,7 @@ "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -1322,6 +1326,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.7.tgz", "integrity": "sha512-m/YsbcBal+gA5CFrDpqXqsSfylo+DIQrkFY3qhVIltsYRfu8ct8J9pqsTO6OPf3mvqdOpFGrV5sBjoyAzOBvsw==", "license": "MIT", + "peer": true, "dependencies": { "axios": "0.27.2", "iterare": "1.2.1", @@ -1467,6 +1472,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.4.7.tgz", "integrity": "sha512-lPE5Ltg2NbQGRQIwXWY+4cNrXhJdycbxFDQ8mNxSIuv+LbrJBIdEB/NONk+LLn9N/8d2+I2LsIETGQrPvsejBg==", "license": "MIT", + "peer": true, "dependencies": { "body-parser": "1.20.0", "cors": "2.8.5", @@ -1916,6 +1922,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1928,6 +1944,7 @@ "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -1997,6 +2014,7 @@ "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" @@ -2409,6 +2427,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-jwt-verify": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/aws-jwt-verify/-/aws-jwt-verify-5.1.1.tgz", + "integrity": "sha512-j6whGdGJmQ27agk4ijY8RPv6itb8JLb7SCJ86fEnneTcSBrpxuwL8kLq6y5WVH95aIknyAloEqAsaOLS1J8ITQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/aws-sdk": { "version": "2.1691.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1691.0.tgz", @@ -2678,6 +2705,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -2904,13 +2932,15 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/class-validator": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/validator": "^13.11.8", "libphonenumber-js": "^1.10.53", @@ -3042,6 +3072,28 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -4400,6 +4452,7 @@ "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -5788,6 +5841,7 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", "license": "MIT", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1" @@ -6118,7 +6172,8 @@ "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/require-directory": { "version": "2.1.1", @@ -6253,6 +6308,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -6947,6 +7003,7 @@ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7117,6 +7174,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", "dev": true, + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", diff --git a/backend/package.json b/backend/package.json index 7a7d9b8..624f4fa 100644 --- a/backend/package.json +++ b/backend/package.json @@ -16,10 +16,12 @@ "@nestjs/jwt": "^8.0.0", "@nestjs/passport": "^8.0.0", "@nestjs/platform-express": "^8.4.7", + "aws-jwt-verify": "^5.1.1", "aws-sdk": "^2.1030.0", "aws-serverless-express": "^3.4.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "cookie-parser": "^1.4.7", "crypto": "^1.0.1", "dotenv": "^16.4.5", "jwt-decode": "^4.0.0", @@ -32,6 +34,7 @@ "@nestjs/testing": "^8.4.7", "@types/aws-lambda": "^8.10.149", "@types/aws-serverless-express": "^3.3.10", + "@types/cookie-parser": "^1.4.10", "@types/jest": "^27.0.0", "@types/node": "^20.0.0", "esbuild": "^0.25.2", diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index ebb5540..178eed6 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -1,6 +1,7 @@ -import { Controller, Post, Body } from '@nestjs/common'; +import { Controller, Post, Body, Res } from '@nestjs/common'; import { AuthService } from './auth.service'; import { User } from '../types/User'; +import { Response } from 'express'; import { UserStatus } from '../../../middle-layer/types/UserStatus'; @Controller('auth') @@ -19,10 +20,10 @@ export class AuthController { @Post('login') async login( + @Res({ passthrough: true }) response: Response, @Body('username') username: string, - @Body('password') password: string, + @Body('password') password: string, ): Promise<{ - access_token?: string; user: User; session?: string; challenge?: string; @@ -30,7 +31,20 @@ export class AuthController { username?: string; position?: string; }> { - return await this.authService.login(username, password); + const result = await this.authService.login(username, password); + + // Set cookie with access token + if (result.access_token) { + response.cookie('access_token', result.access_token, { + httpOnly: true, // Cannot be accessed by JavaScript (XSS protection) + secure: process.env.NODE_ENV === 'production', // Only HTTPS in production + sameSite: 'strict', // CSRF protection + maxAge: 3600000, // 1 hour in milliseconds + path: '/', // Cookie available on all routes + }); + } + delete result.access_token; + return result } @Post('set-password') diff --git a/backend/src/auth/auth.guard.ts b/backend/src/auth/auth.guard.ts new file mode 100644 index 0000000..933eda1 --- /dev/null +++ b/backend/src/auth/auth.guard.ts @@ -0,0 +1,71 @@ +import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common"; +import { Observable } from "rxjs"; +import { CognitoJwtVerifier } from "aws-jwt-verify"; + + + + +@Injectable() +export class RolesGuard implements CanActivate { + private verifier: any; + constructor() { + const userPoolId = process.env.COGNITO_USER_POOL_ID; + if (userPoolId) { + this.verifier = CognitoJwtVerifier.create({ + userPoolId, + tokenUse: "access", + clientId: process.env.COGNITO_CLIENT_ID, + }); + } else { + throw new Error( + "[AUTH] USER POOL ID is not defined in environment variables" + ); + } + } + + async canActivate(context: ExecutionContext): Promise { + try { + const request = context.switchToHttp().getRequest(); + const accessToken = request.cookies["access_token"]; + const result = await this.verifier.verify(accessToken); + + return true; + } catch (error) { + console.error("Token verification failed:", error); // Debug log + return false; + } + } +} + +@Injectable() +export class VerifyAdminRoleGuard implements CanActivate { + private verifier: any; + constructor() { + const userPoolId = process.env.COGNITO_USER_POOL_ID; + if (userPoolId) { + this.verifier = CognitoJwtVerifier.create({ + userPoolId, + tokenUse: "access", + clientId: process.env.COGNITO_CLIENT_ID, + }); + } else { + throw new Error( + "[AUTH] USER POOL ID is not defined in environment variables" + ); + } + } + async canActivate(context: ExecutionContext): Promise { + try { + const request = context.switchToHttp().getRequest(); + const accessToken = request.cookies["access_token"]; + const result = await this.verifier.verify(accessToken); + const groups = result['cognito:groups'] || []; + console.log("User groups from token:", groups); + + return true; + } catch (error) { + console.error("Token verification failed:", error); // Debug log + return false; + } + } +} diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 73225a6..6dde36e 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -350,7 +350,7 @@ private isValidEmail(email: string): boolean { if (!accessToken) { throw new Error("Access token is undefined."); - } + } const getUserResponse = await this.cognito .getUser({ AccessToken: accessToken }) @@ -403,7 +403,7 @@ private isValidEmail(email: string): boolean { user = newUser; } - return { access_token: idToken, user, message: "Login Successful!" }; + return { access_token: accessToken, user, message: "Login Successful!" }; } catch (error: unknown) { /* Login Failures */ const cognitoError = error as AwsCognitoError; diff --git a/backend/src/grant/grant.controller.ts b/backend/src/grant/grant.controller.ts index 71f3e1d..32752c1 100644 --- a/backend/src/grant/grant.controller.ts +++ b/backend/src/grant/grant.controller.ts @@ -1,12 +1,14 @@ -import { Controller, Get, Param, Put, Body, Patch, Post, Delete, ValidationPipe, Logger } from '@nestjs/common'; +import { Controller, Get, Param, Put, Body, Patch, Post, Delete, ValidationPipe, Logger, UseGuards } from '@nestjs/common'; import { GrantService } from './grant.service'; import { Grant } from '../../../middle-layer/types/Grant'; +import { RolesGuard, VerifyAdminRoleGuard } from '../auth/auth.guard'; @Controller('grant') export class GrantController { constructor(private readonly grantService: GrantService) { } @Get() + @UseGuards(VerifyAdminRoleGuard) async getAllGrants() { return await this.grantService.getAllGrants(); } diff --git a/backend/src/main.ts b/backend/src/main.ts index 8939c94..81d76c6 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -3,7 +3,7 @@ import { AppModule } from './app.module'; import * as dotenv from 'dotenv' import AWS from 'aws-sdk'; import { ValidationPipe } from '@nestjs/common'; - +import cookieParser from 'cookie-parser'; /* ! */ async function bootstrap() { AWS.config.update({ @@ -20,6 +20,7 @@ async function bootstrap() { methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], }); + app.use(cookieParser()); app.useGlobalPipes(new ValidationPipe()); await app.listen(3001); diff --git a/frontend/src/main-page/restricted/RestrictedPage.tsx b/frontend/src/main-page/restricted/RestrictedPage.tsx index 3fe0767..f9421e9 100644 --- a/frontend/src/main-page/restricted/RestrictedPage.tsx +++ b/frontend/src/main-page/restricted/RestrictedPage.tsx @@ -1,8 +1,10 @@ import { Link } from "react-router-dom"; import logo from "../../images/bcan_logo.svg"; import { ButtonColorOption } from "../../custom/RingButton"; +import { useAuthContext } from "../../context/auth/authContext"; function RestrictedPage() { + const {logout} = useAuthContext(); return (
@@ -21,6 +23,9 @@ function RestrictedPage() { borderStyle: 'solid', borderColor: 'black', borderWidth: '1px' }} className="py-2 px-4 rounded" + onClick={() => { + logout() + }} > Back to Login From b12f1f93755da39f678768fddae93e10988bf1dd Mon Sep 17 00:00:00 2001 From: prooflesben Date: Sun, 7 Dec 2025 18:58:50 -0500 Subject: [PATCH 2/3] All the routes have guards on them --- backend/src/auth/auth.guard.ts | 9 ++- backend/src/grant/grant.controller.ts | 9 ++- backend/src/grant/grant.service.ts | 1 + .../notifications/notification.controller.ts | 9 ++- backend/src/user/user.controller.ts | 71 ++++++++++--------- 5 files changed, 62 insertions(+), 37 deletions(-) diff --git a/backend/src/auth/auth.guard.ts b/backend/src/auth/auth.guard.ts index 933eda1..68c079c 100644 --- a/backend/src/auth/auth.guard.ts +++ b/backend/src/auth/auth.guard.ts @@ -6,7 +6,7 @@ import { CognitoJwtVerifier } from "aws-jwt-verify"; @Injectable() -export class RolesGuard implements CanActivate { +export class VerifyUserGuard implements CanActivate { private verifier: any; constructor() { const userPoolId = process.env.COGNITO_USER_POOL_ID; @@ -61,8 +61,13 @@ export class VerifyAdminRoleGuard implements CanActivate { const result = await this.verifier.verify(accessToken); const groups = result['cognito:groups'] || []; console.log("User groups from token:", groups); + if (!groups.includes('Admin')) { + console.warn("Access denied: User is not an Admin"); + return false; + } else { + return true; + } - return true; } catch (error) { console.error("Token verification failed:", error); // Debug log return false; diff --git a/backend/src/grant/grant.controller.ts b/backend/src/grant/grant.controller.ts index 32752c1..6d403e2 100644 --- a/backend/src/grant/grant.controller.ts +++ b/backend/src/grant/grant.controller.ts @@ -1,14 +1,14 @@ import { Controller, Get, Param, Put, Body, Patch, Post, Delete, ValidationPipe, Logger, UseGuards } from '@nestjs/common'; import { GrantService } from './grant.service'; import { Grant } from '../../../middle-layer/types/Grant'; -import { RolesGuard, VerifyAdminRoleGuard } from '../auth/auth.guard'; +import { VerifyUserGuard } from '../auth/auth.guard'; @Controller('grant') export class GrantController { constructor(private readonly grantService: GrantService) { } @Get() - @UseGuards(VerifyAdminRoleGuard) + @UseGuards(VerifyUserGuard) async getAllGrants() { return await this.grantService.getAllGrants(); } @@ -16,6 +16,7 @@ export class GrantController { @Put('inactivate') + @UseGuards(VerifyUserGuard) async inactivate( @Body('grantIds') grantIds: number[] ): Promise { @@ -29,6 +30,7 @@ export class GrantController { } @Post('new-grant') + @UseGuards(VerifyUserGuard) async addGrant( @Body(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true })) grant: Grant @@ -37,15 +39,18 @@ export class GrantController { } @Put('save') + @UseGuards(VerifyUserGuard) async saveGrant(@Body() grantData: Grant) { return await this.grantService.updateGrant(grantData) } @Delete(':grantId') + @UseGuards(VerifyUserGuard) async deleteGrant(@Param('grantId') grantId: number) { return await this.grantService.deleteGrantById(grantId); } @Get(':id') + @UseGuards(VerifyUserGuard) async getGrantById(@Param('id') GrantId: string) { return await this.grantService.getGrantById(parseInt(GrantId, 10)); } diff --git a/backend/src/grant/grant.service.ts b/backend/src/grant/grant.service.ts index 23b096f..753f587 100644 --- a/backend/src/grant/grant.service.ts +++ b/backend/src/grant/grant.service.ts @@ -15,6 +15,7 @@ export class GrantService { // function to retrieve all grants in our database async getAllGrants(): Promise { + console.log("GETTING ALL GRANTS"); // loads in the environment variable for the table now const params = { TableName: process.env.DYNAMODB_GRANT_TABLE_NAME || 'TABLE_FAILURE', diff --git a/backend/src/notifications/notification.controller.ts b/backend/src/notifications/notification.controller.ts index f0aadfe..df076ab 100644 --- a/backend/src/notifications/notification.controller.ts +++ b/backend/src/notifications/notification.controller.ts @@ -1,6 +1,7 @@ -import { Controller, Post, Body, Get, Query, Param, Patch, Put, Delete } from '@nestjs/common'; +import { Controller, Post, Body, Get, Query, Param, Patch, Put, Delete, UseGuards } from '@nestjs/common'; import { NotificationService } from './notification.service'; import { Notification } from '../../../middle-layer/types/Notification'; +import { VerifyUserGuard } from '../auth/auth.guard'; @Controller('notifications') @@ -10,6 +11,7 @@ export class NotificationController { // allows to create a new notification @Post() + @UseGuards(VerifyUserGuard) async create(@Body() notification: Partial): Promise { // call the service's createNotification method and return the result return await this.notificationService.createNotification(notification as Notification); @@ -17,17 +19,20 @@ export class NotificationController { // gets notifications based on the noticationId @Get(':notificationId') + @UseGuards(VerifyUserGuard) async findByNotification(@Param('notificationId') notificationId: string) { return await this.notificationService.getNotificationByNotificationId(notificationId); } @Get('/user/:userId/current') + @UseGuards(VerifyUserGuard) async findCurrentByUser(@Param('userId') userId: string) { return await this.notificationService.getCurrentNotificationsByUserId(userId); } // gets notifications by user id (sorted by most recent notifications first) @Get('/user/:userId') + @UseGuards(VerifyUserGuard) async findByUser(@Param('userId') userId: string) { console.log("HERE") return await this.notificationService.getNotificationByUserId(userId); @@ -35,6 +40,7 @@ export class NotificationController { // updates notification by its id @Put(':notificationId') + @UseGuards(VerifyUserGuard) async updateNotification(@Param('notificationId') notificationId: string, @Body() notification: Partial){ return await this.notificationService.updateNotification(notificationId, notification); @@ -47,6 +53,7 @@ export class NotificationController { * @param notificationId the id of the notification to delete */ @Delete(':notificationId') + @UseGuards(VerifyUserGuard) async deleteNotification(@Param('notificationId') notificationId: string) { return await this.notificationService.deleteNotification(notificationId); } diff --git a/backend/src/user/user.controller.ts b/backend/src/user/user.controller.ts index 75b18ff..79439a6 100644 --- a/backend/src/user/user.controller.ts +++ b/backend/src/user/user.controller.ts @@ -1,53 +1,60 @@ -import { Controller, Get, Post, Body, Param } from '@nestjs/common'; -import { UserService } from './user.service'; -import { User } from '../../../middle-layer/types/User'; -import { UserStatus } from '../../../middle-layer/types/UserStatus'; +import { Controller, Get, Post, Body, Param, UseGuards } from "@nestjs/common"; +import { UserService } from "./user.service"; +import { User } from "../../../middle-layer/types/User"; +import { UserStatus } from "../../../middle-layer/types/UserStatus"; +import { VerifyAdminRoleGuard, VerifyUserGuard } from "../auth/auth.guard"; -@Controller('user') +@Controller("user") export class UserController { constructor(private readonly userService: UserService) {} @Get() + @UseGuards(VerifyUserGuard) async getAllUsers() { return await this.userService.getAllUsers(); } - - @Get('inactive') + @Get("inactive") + @UseGuards(VerifyUserGuard) async getAllInactiveUsers(): Promise { return await this.userService.getAllInactiveUsers(); } - @Get('active') + @Get("active") + @UseGuards(VerifyUserGuard) async getAllActiveUsers(): Promise { console.log("Fetching all active users"); return await this.userService.getAllActiveUsers(); } - // Make sure to put a guard on this route - @Post('change-role') - async addToGroup( - @Body('user') user: User, - @Body('groupName') groupName: UserStatus, - @Body('requestedBy') requestedBy: User, - ): Promise< User > { - let newUser:User = await this.userService.addUserToGroup(user, groupName,requestedBy); - return newUser as User; - } + // Make sure to put a guard on this route + @Post("change-role") + @UseGuards(VerifyAdminRoleGuard) + async addToGroup( + @Body("user") user: User, + @Body("groupName") groupName: UserStatus, + @Body("requestedBy") requestedBy: User + ): Promise { + let newUser: User = await this.userService.addUserToGroup( + user, + groupName, + requestedBy + ); + return newUser as User; + } - @Post('delete-user') - async deleteUser( - @Body('user') user: User, - @Body('requestedBy') requestedBy: User, - ): Promise { - let deletedUser = await this.userService.deleteUser(user, requestedBy); - return user as User; - } + @Post("delete-user") + @UseGuards(VerifyAdminRoleGuard) + async deleteUser( + @Body("user") user: User, + @Body("requestedBy") requestedBy: User + ): Promise { + let deletedUser = await this.userService.deleteUser(user, requestedBy); + return user as User; + } - @Get(':id') - async getUserById(@Param('id') userId: string) { + @Get(":id") + @UseGuards(VerifyUserGuard) + async getUserById(@Param("id") userId: string) { return await this.userService.getUserById(userId); } - - - -} \ No newline at end of file +} From 3c1960718328dbdb80889fef24ed55ce52d23ba7 Mon Sep 17 00:00:00 2001 From: prooflesben Date: Sun, 7 Dec 2025 19:20:18 -0500 Subject: [PATCH 3/3] added back to login on restricted page --- frontend/src/RegisterLanding.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frontend/src/RegisterLanding.tsx b/frontend/src/RegisterLanding.tsx index cab1248..4af03c5 100644 --- a/frontend/src/RegisterLanding.tsx +++ b/frontend/src/RegisterLanding.tsx @@ -1,9 +1,13 @@ +import { Link } from "react-router-dom"; import logo from "./images/bcan_logo.svg"; +import { ButtonColorOption } from "./custom/RingButton"; +import { useAuthContext } from "./context/auth/authContext"; /** * Registered user landing page after signing up */ const RegisterLanding = () => { + const {logout} = useAuthContext(); return (
@@ -25,6 +29,21 @@ const RegisterLanding = () => { our team. You'll receive an email notification once your account has been approved. Please try logging in after receiving approval. + + +