From 1073b794d691f07770ecb227b5f6395fa9e7141a Mon Sep 17 00:00:00 2001 From: Roy Dumblauskas Date: Tue, 7 Jul 2026 01:24:14 -0500 Subject: [PATCH] adding permissions layer --- backend/src/db/schema.ts | 26 +++++++++++++++++++++----- backend/src/helpers/authenticate.ts | 17 +++++++++++++++++ backend/src/routes/articles.ts | 18 +++++++++++++----- backend/src/routes/auth.ts | 2 +- 4 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 backend/src/helpers/authenticate.ts diff --git a/backend/src/db/schema.ts b/backend/src/db/schema.ts index 09d281f..d1117dc 100644 --- a/backend/src/db/schema.ts +++ b/backend/src/db/schema.ts @@ -10,7 +10,8 @@ import { unique, boolean, smallint, - check + check, + foreignKey } from "drizzle-orm/pg-core"; import { sql, defineRelations } from "drizzle-orm"; @@ -42,11 +43,26 @@ export const articles = pgTable("articles", { date_created: timestamp({ mode: "date", withTimezone: true }).defaultNow().notNull(), last_edit: timestamp({ mode: "date", withTimezone: true }).defaultNow().notNull(), date_published: timestamp({ mode: "date", withTimezone: true }), + + previous_article: uuid(), + next_article: uuid() }, - (table) => [{ - slugIdx: unique("no_repeat_slugs_per_author") - .on(table.author_id, table.slug) - }]); + (table) => [ + unique("no_repeat_slugs_per_author") + .on(table.author_id, table.slug), + + foreignKey({ + name: "next_article_ref_id", + columns: [table.next_article], + foreignColumns: [table.id] + }), + + foreignKey({ + name: "previous_article_ref_id", + columns: [table.previous_article], + foreignColumns: [table.id] + }) + ]); export const articleBlocks = pgTable("article_blocks", { id: uuid().notNull().primaryKey().defaultRandom(), diff --git a/backend/src/helpers/authenticate.ts b/backend/src/helpers/authenticate.ts new file mode 100644 index 0000000..19a847b --- /dev/null +++ b/backend/src/helpers/authenticate.ts @@ -0,0 +1,17 @@ +import { Request, Response, NextFunction } from "express"; +import JsonWebToken from '../types/JsonWebToken.ts'; + +export enum Permission { + read = 1, + write = 2, + delete = 4 +}; + +export function requirePermissions(permission: number, isOwner: Boolean, requiredPermissions: number): Boolean { + const mask = isOwner ? + Math.floor(permission / 10) : + permission % 10; + + return (mask & requiredPermissions) === requiredPermissions; + +}; diff --git a/backend/src/routes/articles.ts b/backend/src/routes/articles.ts index 60d5c14..0f7f40d 100644 --- a/backend/src/routes/articles.ts +++ b/backend/src/routes/articles.ts @@ -2,6 +2,7 @@ import { drizzle } from 'drizzle-orm/node-postgres'; import { eq } from 'drizzle-orm'; import { articles } from '../db/schema.ts'; import express, { Request, Response } from "express"; +import { Permission, requirePermissions } from '../helpers/authenticate.ts'; const db = drizzle(process.env.DATABASE_URL!); export const articlesRouter = express.Router(); @@ -18,6 +19,17 @@ articlesRouter.get("/getArticle/:articleId", async (req: Request, res: Response) const resp = await db.select() .from(articles) .where(eq(articles.id, parsedId)); + + if (!requirePermissions( + req.jwt.payload.prm, + req.jwt.payload.uid === resp[0].author_id, + Permission.read)) { + + res.status(403).json({ + error: "Insufficient Permissions" + }); + }; + res.json(resp); }); @@ -47,11 +59,7 @@ articlesRouter.put("/updateArticle/:articleId", async (req: Request, res: Respon const resp = await db.update(articles) .set({ - name: req.body.name, - author_id: req.body.author_id, - slug: req.body.name.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, ''), - summary: req.body.summary, - article_status: req.body.article_status, + ...req.body, last_edit: new Date(Date.now()), date_published: req.body.article_status == "published" ? new Date(Date.now()) : null }) diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index e3f333e..9936d02 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -58,7 +58,7 @@ authRouter.post("/signup", async (req: Request, res: Response) => { display_name: display_name, username: username, hashed_password: hashedPassword, - permissions: 44 + permissions: 71 }); } catch (err: any) { if (err.cause.code === "23505") {