From 387a9a36f3419bf58214a8f8296cf86b4c34fe35 Mon Sep 17 00:00:00 2001 From: jd Date: Fri, 13 Feb 2026 22:06:49 +0000 Subject: [PATCH] Refactor & unit tests --- .env.test | 2 + API Tests/Auth.http | 4 +- API Tests/User.http | 2 +- bunfig.toml | 5 ++ endpoints/auth.ts | 29 ------- endpoints/user.ts | 28 ------ package.json | 8 +- src/endpoints/auth.ts | 32 +++++++ src/endpoints/user.ts | 38 ++++++++ index.ts => src/index.ts | 9 +- src/orm/claims.ts | 30 +++++++ src/orm/orm.ts | 16 ++++ utilities/orm.ts => src/orm/user.ts | 84 +++++++----------- src/tests/global-mocks.ts | 2 + src/tests/test-setup.ts | 35 ++++++++ src/tests/user.test.ts | 130 ++++++++++++++++++++++++++++ src/utilities/errors.ts | 17 ++++ src/utilities/guard.ts | 57 ++++++++++++ src/utilities/responseHelper.ts | 18 ++++ utilities/guard.ts | 25 ------ 20 files changed, 423 insertions(+), 148 deletions(-) create mode 100644 .env.test create mode 100644 bunfig.toml delete mode 100644 endpoints/auth.ts delete mode 100644 endpoints/user.ts create mode 100644 src/endpoints/auth.ts create mode 100644 src/endpoints/user.ts rename index.ts => src/index.ts (77%) create mode 100644 src/orm/claims.ts create mode 100644 src/orm/orm.ts rename utilities/orm.ts => src/orm/user.ts (63%) create mode 100644 src/tests/global-mocks.ts create mode 100644 src/tests/test-setup.ts create mode 100644 src/tests/user.test.ts create mode 100644 src/utilities/errors.ts create mode 100644 src/utilities/guard.ts create mode 100644 src/utilities/responseHelper.ts delete mode 100644 utilities/guard.ts diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..81a7c9a --- /dev/null +++ b/.env.test @@ -0,0 +1,2 @@ +DATABASE_URL=postgres://admin:iiyama12@192.168.1.166:5432/bgAppTest +JWT_SECRET_KEY=MySecret \ No newline at end of file diff --git a/API Tests/Auth.http b/API Tests/Auth.http index afe8092..f146582 100644 --- a/API Tests/Auth.http +++ b/API Tests/Auth.http @@ -2,11 +2,11 @@ POST http://localhost:3000/api/auth/login Content-Type: application/json { - "username": "jd2", + "username": "jd", "password": "Foobar" } ### GET http://localhost:3000/api/auth/test Content-Type: application/json -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIyIiwiY2xhaW1zIjpbIlVTRVJTX1NFTEZfUkVBRCJdLCJpYXQiOjE3NzA5MzgwNzcsImV4cCI6MTc3MTAyNDQ3N30.T_LInbvYJkv1beS39TSuC3asGtbx6gO2bKnFDk52qXM +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxIiwiY2xhaW1zIjpbIkFETUlOIiwiVVNFUlNfQ1JFQVRFIiwiVVNFUlNfT1RIRVJfVVBEQVRFIiwiVVNFUlNfU0VMRl9SRUFEIiwiVVNFUlNfU0VMRl9VUERBVEUiLCJVU0VSU19PVEhFUl9SRUFEIiwiVVNFUlNfU0VMRl9ERUxFVEUiXSwiaWF0IjoxNzcxMDEyNDM5LCJleHAiOjE3NzEwOTg4Mzl9.__EHi3dO_uG1mtCVhmRqVKTkbTkOzM5Hu-4gMrIfu7I diff --git a/API Tests/User.http b/API Tests/User.http index 68d5f31..cc3f2ff 100644 --- a/API Tests/User.http +++ b/API Tests/User.http @@ -9,4 +9,4 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxIiwiY ### GET http://localhost:3000/api/user/2 -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIyIiwiY2xhaW1zIjpbIlVTRVJTX1NFTEZfUkVBRCJdLCJpYXQiOjE3NzA5MzE1NzYsImV4cCI6MTc3MTAxNzk3Nn0.6ITZfX3_vtAFaf6qTuX7Fk_RLomgKbeto9IWhGMdN-k \ No newline at end of file +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxIiwiY2xhaW1zIjpbIkFETUlOIiwiVVNFUlNfQ1JFQVRFIiwiVVNFUlNfT1RIRVJfVVBEQVRFIiwiVVNFUlNfU0VMRl9SRUFEIiwiVVNFUlNfU0VMRl9VUERBVEUiLCJVU0VSU19PVEhFUl9SRUFEIiwiVVNFUlNfU0VMRl9ERUxFVEUiXSwiaWF0IjoxNzcxMDE5NzMzLCJleHAiOjE3NzExMDYxMzN9.V4La9Sv13M15lubtxWGESUksWldhlyG8AhiIE4zAtWo \ No newline at end of file diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..d1120ec --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,5 @@ +telemetry = false + +[test] +root = "tests" +preload = ["./src/tests/test-setup.ts", "./src/tests/global-mocks.ts"] \ No newline at end of file diff --git a/endpoints/auth.ts b/endpoints/auth.ts deleted file mode 100644 index bf320d1..0000000 --- a/endpoints/auth.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {BunRequest as Request} from "bun"; -import {Claims, orm} from "../utilities/orm.ts"; -import jwt from "jsonwebtoken"; - -async function login(request: Request): Promise { - try { - const requestBody = await request.json(); - console.log(`/api/auth/login: username=${requestBody.username}`); - const claims: Claims | null = await orm.users.verify(requestBody.username, requestBody.password); - console.log(claims); - if (claims) { - const token = jwt.sign({...claims}, process.env.JWT_SECRET_KEY as string, {expiresIn: "24h"}); - return Response.json({token: token}, {status: 200}); - } - - return Response.json({token: null}, {status: 401}); - } catch (e) { - console.log(e); - return Response.json({message: e}, {status: 401}); - } -} -async function test(request: Request, claims: Claims) { - return Response.json(claims, {status: 200}); -} - -export default { - login, - test -}; \ No newline at end of file diff --git a/endpoints/user.ts b/endpoints/user.ts deleted file mode 100644 index 997255c..0000000 --- a/endpoints/user.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Claims, orm} from "../utilities/orm"; -import {BunRequest as Request} from 'bun'; - -async function create (request: Request, claims: Claims): Promise { - try { - const requestBody = await request.json(); - return Response.json({ - ...(await orm.users.create(requestBody.username, requestBody.password, claims)) - }, {status: 200}); - } catch (e: any) { - return Response.json({message: e.message}, {status: 500}); - } -} - -async function get(request: Request, claims:Claims): Promise { - try { - return Response.json({ - ...(await orm.users.get(request.params.id, claims)) - }, {status: 200}); - } catch (e: any) { - return Response.json({message: e.message}, {status: 500}); - } -} - -export default { - create, - get, -} \ No newline at end of file diff --git a/package.json b/package.json index 1a69d11..0b740f2 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "bgApp", "version": "1.0.0", "description": "", - "main": "index.ts", + "main": "src/index.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "bun build index.ts --target bun", - "dev": "bun --env-file=.env.dev run index.ts" + "test": "bun --env-file=.env.test test ./src/tests/", + "build": "bun build src/index.ts --target bun", + "dev": "bun --env-file=.env.dev run src/index.ts" }, "private": true, "dependencies": { diff --git a/src/endpoints/auth.ts b/src/endpoints/auth.ts new file mode 100644 index 0000000..92b5039 --- /dev/null +++ b/src/endpoints/auth.ts @@ -0,0 +1,32 @@ +import {BunRequest as Request} from "bun"; +import {orm} from "../orm/orm.ts"; +import jwt from "jsonwebtoken"; +import {UnwrappedRequest} from "../utilities/guard"; +import {ErrorResponse} from "../utilities/responseHelper"; +import {Claims} from "../orm/claims"; +import {UnauthorizedError} from "../utilities/errors"; + +async function login(request: UnwrappedRequest): Promise { + try { + const requestBody = request.json; + console.log(`/api/auth/login: username=${requestBody.username}`); + const claims: Claims | null = await orm.users.verify(requestBody.username, requestBody.password); + console.log(claims); + if (claims) { + const token = jwt.sign({...claims}, process.env.JWT_SECRET_KEY as string, {expiresIn: "24h"}); + return Response.json({token: token, claims: claims}, {status: 200}); + } + + throw new UnauthorizedError('Invalid credentials'); + } catch (error: any) { + return new ErrorResponse(error as Error); + } +} +async function test(request: UnwrappedRequest) { + return Response.json(request.claims, {status: 200}); +} + +export default { + login, + test +}; \ No newline at end of file diff --git a/src/endpoints/user.ts b/src/endpoints/user.ts new file mode 100644 index 0000000..d38477e --- /dev/null +++ b/src/endpoints/user.ts @@ -0,0 +1,38 @@ +import {orm} from "../orm/orm.ts"; +import {UnwrappedRequest} from "../utilities/guard.ts"; +import {ErrorResponse} from "../utilities/responseHelper.ts"; + +async function create(request: UnwrappedRequest): Promise { + try { + const requestBody = request.json; + + const newUser = await orm.users.create(requestBody.username, requestBody.password, request.claims); + if(!newUser) { + return new Response(null,{status: 201}) + } + + return Response.json( + { + ...newUser + }, + {status: 201} + ); + } catch (error: any) { + return new ErrorResponse(error as Error); + } +} + +async function get(request: UnwrappedRequest): Promise { + try { + return Response.json({ + ...(await orm.users.get(request.params.id, request.claims)) + }, {status: 200}); + } catch (error: any) { + return new ErrorResponse(error as Error); + } +} + +export default { + create, + get, +} \ No newline at end of file diff --git a/index.ts b/src/index.ts similarity index 77% rename from index.ts rename to src/index.ts index 09d5ac8..71dda7f 100644 --- a/index.ts +++ b/src/index.ts @@ -1,12 +1,11 @@ -import argon2 from "argon2"; -import {guard} from './utilities/guard'; -import auth from "./endpoints/auth"; -import user from "./endpoints/user"; +import {unwrapMethod, guard} from './utilities/guard.ts'; +import auth from "./endpoints/auth.ts"; +import user from "./endpoints/user.ts"; const server = Bun.serve({ routes: { "/api/auth/login": { - POST: auth.login, + POST: unwrapMethod(auth.login), }, "/api/auth/test": { GET: guard(auth.test, ['ADMIN', 'USERS_OTHER_DELETE']) diff --git a/src/orm/claims.ts b/src/orm/claims.ts new file mode 100644 index 0000000..34e1301 --- /dev/null +++ b/src/orm/claims.ts @@ -0,0 +1,30 @@ +import {sql} from 'bun'; + +export class Claims { + userId: string | undefined; + claims: string[] = []; + + public static test(userClaims: Claims, guardClaim: string): Boolean { + return userClaims.claims.some(x => x === guardClaim); + } +} + +export class ClaimsOrm { + async getByUserId(userId: string): Promise { + const dbResults: any[] = await sql`SELECT c.name + from user_claims as uc + JOIN claims as c on uc.claimid = c.id + where uc.userid = ${userId};`; + const claims = new Claims(); + claims.userId = userId; + claims.claims = dbResults.map(x => x.name); + return claims; + } + + async getDefaultClaims(): Promise { + const dbResults: any[] = await sql`SELECT id + FROM claims + WHERE is_default = true;`; + return dbResults.map(x => x.id); + } +} \ No newline at end of file diff --git a/src/orm/orm.ts b/src/orm/orm.ts new file mode 100644 index 0000000..ba547fd --- /dev/null +++ b/src/orm/orm.ts @@ -0,0 +1,16 @@ +import {ClaimsOrm} from "./claims"; +import {UsersOrm} from "./user"; + + +class Orm { + claims: ClaimsOrm; + users: UsersOrm; + + constructor() { + this.claims = new ClaimsOrm(); + this.users = new UsersOrm(this.claims); + } + +} + +export const orm = new Orm(); \ No newline at end of file diff --git a/utilities/orm.ts b/src/orm/user.ts similarity index 63% rename from utilities/orm.ts rename to src/orm/user.ts index b394ab4..3b0cae8 100644 --- a/utilities/orm.ts +++ b/src/orm/user.ts @@ -1,37 +1,10 @@ -import argon2 from "argon2"; -import {first} from "lodash"; +import {Claims, ClaimsOrm} from "./claims"; import {sql} from "bun"; +import {first} from "lodash"; +import argon2 from "argon2"; +import {BadRequestError, NotFoundError, UnauthorizedError} from "../utilities/errors"; -export class Claims { - userId: string | undefined; - claims: string[] = []; - - public static test(userClaims: Claims, guardClaim: string): Boolean { - return userClaims.claims.some(x => x === guardClaim); - } -} - -class ClaimsOrm { - async getByUserId(userId: string): Promise { - const dbResults: any[] = await sql`SELECT c.name - from user_claims as uc - JOIN claims as c on uc.claimid = c.id - where uc.userid = ${userId};`; - const claims = new Claims(); - claims.userId = userId; - claims.claims = dbResults.map(x => x.name); - return claims; - } - - async getDefaultClaims(): Promise { - const dbResults: any[] = await sql`SELECT id - FROM claims - WHERE is_default = true;`; - return dbResults.map(x => x.id); - } -} - -class User { +export class User { id: string; name: string; isAdmin: boolean; @@ -45,7 +18,7 @@ class User { } } -class UsersOrm { +export class UsersOrm { #claims: ClaimsOrm; constructor(claims: ClaimsOrm) { @@ -58,13 +31,20 @@ class UsersOrm { Claims.test(claims, 'USERS_OTHER_READ') || (Claims.test(claims, 'USERS_SELF_READ') && id === claims.userId) )) { - throw new Error('Unauthorized'); + throw new + UnauthorizedError(); } const dbResult: any = first(await sql`select * from users where id = ${id} - and is_active = true limit 1`); + and is_active = true + limit 1`); + + if(!dbResult) { + throw new NotFoundError('No matching user exists'); + } + return new User(dbResult.id, dbResult.username, dbResult.is_admin); } @@ -72,24 +52,25 @@ class UsersOrm { try { const dbResult: any = first(await sql`select * from users - where username = ${username} limit 1`); + where username = ${username} + limit 1`); if (!await argon2.verify(dbResult.pass_hash, password)) { return null; } return this.#claims.getByUserId(dbResult.id); } catch (error) { - console.log(error); throw error; } } - async create(username: string, password: string, claims: Claims): Promise { + async create(username: string, password: string, claims: Claims): Promise { const existingUser: any = first(await sql`SELECT id FROM users - WHERE username = ${username} LIMIT 1`); + WHERE username = ${username} + LIMIT 1`); if (existingUser) { - throw new Error(`User with id ${existingUser.id} already exists`); + throw new BadRequestError(`User ${username} already exists`); } const defaultClaims: number[] = await this.#claims.getDefaultClaims(); @@ -103,19 +84,14 @@ class UsersOrm { VALUES (${newUserId}, ${defaultClaims[i]})`; } }) + + if (!( + Claims.test(claims, 'ADMIN') || + Claims.test(claims, 'USERS_OTHER_READ') + )) { + return null; + } + return await this.get(newUserId, claims); } -} - -class Orm { - claims: ClaimsOrm; - users: UsersOrm; - - constructor() { - this.claims = new ClaimsOrm(); - this.users = new UsersOrm(this.claims); - } - -} - -export const orm = new Orm(); \ No newline at end of file +} \ No newline at end of file diff --git a/src/tests/global-mocks.ts b/src/tests/global-mocks.ts new file mode 100644 index 0000000..b5bb4f5 --- /dev/null +++ b/src/tests/global-mocks.ts @@ -0,0 +1,2 @@ +import {expect, test, beforeAll} from 'bun:test'; +import {sql} from "bun"; diff --git a/src/tests/test-setup.ts b/src/tests/test-setup.ts new file mode 100644 index 0000000..b8a14e0 --- /dev/null +++ b/src/tests/test-setup.ts @@ -0,0 +1,35 @@ +import {beforeAll, afterAll} from 'bun:test'; +import Bun from 'bun'; +import {sql} from "bun"; + +beforeAll(async () => { + console.log(process.env.DATABASE_URL); + const scriptFile = await Bun.file('./scripts/dbCreate.sql').text(); + + // Drop the database in preparation for rebuild + await sql`DROP SCHEMA public CASCADE`; + await sql`CREATE SCHEMA public`; + + // Run DB build script + await sql.unsafe(scriptFile); + + // Populate initial data + await sql`SET search_path TO showfinder,public`; + await sql`INSERT INTO claims(name, is_default) VALUES ('ADMIN', false)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_CREATE', false)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_OTHER_UPDATE', false)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_OTHER_DELETE', true)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_SELF_READ', true)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_SELF_UPDATE', true)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_OTHER_READ', true)`; + await sql`INSERT INTO claims(name, is_default) VALUES ('USERS_SELF_DELETE', false)`; +}); + + + + + + + + + diff --git a/src/tests/user.test.ts b/src/tests/user.test.ts new file mode 100644 index 0000000..b1af5be --- /dev/null +++ b/src/tests/user.test.ts @@ -0,0 +1,130 @@ +import {expect, test} from 'bun:test'; +import user from '../endpoints/user'; +import {UnwrappedRequest} from "../utilities/guard"; +import {Claims} from "../orm/claims"; + +test('Create user as admin', async () => { + const claims = new Claims(); + claims.claims.push('ADMIN'); + + const request = new UnwrappedRequest({ + claims, + request: null, + json: { + username: 'test1', + password: 'test123', + }, + params: {}, + }); + + const response = await user.create(request); + expect(response.status).toBe(201); + expect(response.body).toBeDefined(); +}); + +test('Create user without read access', async () => { + const claims = new Claims(); + claims.claims.push('USERS_CREATE'); + + const request = new UnwrappedRequest({ + claims, + request: null, + json: { + username: 'test2', + password: 'test123', + }, + params: {}, + }); + + const response = await user.create(request); + expect(response.status).toBe(201); + expect(response.body).toBeNull(); +}); + +test('Create user that already exists', async () => { + const claims = new Claims(); + claims.claims.push('USERS_CREATE'); + + const request = new UnwrappedRequest({ + claims, + request: null, + json: { + username: 'test2', + password: 'test123', + }, + params: {}, + }); + + const response = await user.create(request); + expect(response.status).toBe(400); +}); + +test('Get user', async () => { + const claims = new Claims(); + claims.claims.push('USERS_OTHER_READ'); + + const request = new UnwrappedRequest({ + claims, + request: null, + params: { + id: 1 + }, + }); + + const response = await user.get(request); + const retrievedUser = await response.json(); + expect(response.status).toBe(200); + expect(retrievedUser.id).toBe('1'); +}); + +test('Get user self with only self read permission', async () => { + const claims = new Claims(); + claims.userId = "1"; + claims.claims.push('USERS_OTHER_READ'); + + const request = new UnwrappedRequest({ + claims, + request: null, + params: { + id: 1 + }, + }); + + const response = await user.get(request); + const retrievedUser = await response.json(); + expect(response.status).toBe(200); + expect(retrievedUser.id).toBe('1'); +}); + +test('Get other user without read permissions', async () => { + const claims = new Claims(); + claims.userId = "2"; + claims.claims.push('USERS_SELF_READ'); + + const request = new UnwrappedRequest({ + claims, + request: null, + params: { + id: 1 + }, + }); + + const response = await user.get(request); + expect(response.status).toBe(401); +}); + +test('Get user that doesn\'t exist', async () => { + const claims = new Claims(); + claims.claims.push('ADMIN'); + + const request = new UnwrappedRequest({ + claims, + request: null, + params: { + id: 101 + }, + }); + + const response = await user.get(request); + expect(response.status).toBe(404); +}); \ No newline at end of file diff --git a/src/utilities/errors.ts b/src/utilities/errors.ts new file mode 100644 index 0000000..235cc00 --- /dev/null +++ b/src/utilities/errors.ts @@ -0,0 +1,17 @@ +export class BadRequestError extends Error { + constructor(message: string | undefined = undefined) { + super(message); + } +} + +export class UnauthorizedError extends Error { + constructor(message: string | undefined = undefined) { + super(message); + } +} + +export class NotFoundError extends Error { + constructor(message: string | undefined = undefined) { + super(message); + } +} \ No newline at end of file diff --git a/src/utilities/guard.ts b/src/utilities/guard.ts new file mode 100644 index 0000000..09c9acf --- /dev/null +++ b/src/utilities/guard.ts @@ -0,0 +1,57 @@ +import {BunRequest as Request} from 'bun'; +import jwt from 'jsonwebtoken'; +import {Claims} from "../orm/orm.ts"; + +export function guardRedirect(method: Function, redirectMethod: Function, guardedClaims: string[] | undefined = undefined) { + try { + return guard(method, guardedClaims); + } catch (e) { + return redirectMethod(); + } +} + +export function guard(method: Function, guardedClaims: string[] | undefined = undefined):(r:Request)=>Promise { + return async (request: Request): Promise => { + const authHeader: string | null = request.headers.get('Authorization')?.replace(/^Bearer /, '') as string ?? null; + try { + const userClaims: Claims = jwt.verify(authHeader as string, process.env.JWT_SECRET_KEY as string) as Claims; + console.log('Claims?', guardedClaims !== undefined, !userClaims.claims.some(x => guardedClaims?.includes(x))) + if (guardedClaims !== undefined && !userClaims.claims.some(x => guardedClaims.includes(x))) { + throw new Error('Unauthorized'); + } + return method(await unwrap(request, userClaims)); + } catch (e) { + return Response.json({message: 'Authentication failed.'}, {status: 401}) + } + } +} + +export class UnwrappedRequest { + readonly json: any; + readonly request: Request; + readonly params: { [x: string]: string }; + readonly claims: Claims; + + constructor(input: any) { + this.json = input.json; + this.request = input.request; + this.claims = input.claims; + this.params = input.params; + } +} + +export async function unwrap(request: Request, claims: Claims | null = null) { + return new UnwrappedRequest({ + request, + claims, + json: request.body ? await request.json() : null, + params: request.params, + }) +} + +export function unwrapMethod(methodToUnwrap:((r:UnwrappedRequest)=>Response)|((r:UnwrappedRequest)=>Promise)):(r:Request)=>Promise { + return async (request: Request) => { + const unwrappedRequest = await unwrap(request); + return await methodToUnwrap(unwrappedRequest); + }; +} \ No newline at end of file diff --git a/src/utilities/responseHelper.ts b/src/utilities/responseHelper.ts new file mode 100644 index 0000000..14a6247 --- /dev/null +++ b/src/utilities/responseHelper.ts @@ -0,0 +1,18 @@ +import {BadRequestError, NotFoundError, UnauthorizedError} from "./errors"; + +export class ErrorResponse extends Response { + //@ts-ignore + constructor(error: Error) { + if(error instanceof BadRequestError) { + return Response.json({message: error.message}, {status: 400}); + } + else if(error instanceof UnauthorizedError){ + return Response.json({message: error.message}, {status: 401}); + } + else if(error instanceof NotFoundError){ + return Response.json({message: error.message}, {status: 404}); + } + + return Response.json({message: error.message}, {status: 500}); + } +} \ No newline at end of file diff --git a/utilities/guard.ts b/utilities/guard.ts deleted file mode 100644 index 559f16f..0000000 --- a/utilities/guard.ts +++ /dev/null @@ -1,25 +0,0 @@ -import jwt from 'jsonwebtoken'; -import {Claims} from "./orm"; - -export function guardRedirect(method: Function, redirectMethod: Function, guardedClaims: string[] | undefined = undefined) { - try { - return guard(method, guardedClaims); - } catch (e) { - return redirectMethod(); - } -} - -export function guard(method: Function, guardedClaims: string[] | undefined = undefined) { - return (request: Request): any => { - const authHeader: string | null = request.headers.get('Authorization')?.replace(/^Bearer /, '') as string ?? null; - try { - const userClaims: Claims = jwt.verify(authHeader as string, process.env.JWT_SECRET_KEY as string) as Claims; - if (guardedClaims !== undefined && !userClaims.claims.some(x => guardedClaims.includes(x))) { - throw new Error('Unauthorized'); - } - return method(request, userClaims); - } catch (e) { - return Response.json({message: 'Authentication failed.'}, {status: 401}) - } - } -}