Skip to content

Commit afabe72

Browse files
committed
feat(auth): add permission support to user creation
- Added permissions handling to user registration flow. - Updated controller and service to accept and store Permission[] in Prisma. - New users (doctor, receptionist, etc.) persist their assigned permissions correctly.
1 parent 145caad commit afabe72

File tree

9 files changed

+93
-18
lines changed

9 files changed

+93
-18
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- CreateEnum
2+
CREATE TYPE "Permission" AS ENUM ('MANAGE_USERS', 'MANAGE_ROLES', 'VIEW_PATIENTS', 'EDIT_PATIENTS', 'VIEW_APPOINTMENTS', 'EDIT_APPOINTMENTS', 'VIEW_DASHBOARD');
3+
4+
-- AlterTable
5+
ALTER TABLE "Patient" ADD COLUMN "permissions" "Permission"[];
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `permissions` on the `Patient` table. All the data in the column will be lost.
5+
6+
*/
7+
-- AlterTable
8+
ALTER TABLE "Patient" DROP COLUMN "permissions";
9+
10+
-- AlterTable
11+
ALTER TABLE "User" ADD COLUMN "permissions" "Permission"[];

prisma/schema.prisma

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ model User {
1414
role String
1515
createdAt DateTime @default(now())
1616
updatedAt DateTime @updatedAt
17+
permissions Permission[]
1718
}
1819

1920
enum Role {
@@ -22,13 +23,23 @@ enum Role {
2223
RECEPTIONIST
2324
}
2425

26+
enum Permission {
27+
MANAGE_USERS
28+
MANAGE_ROLES
29+
VIEW_PATIENTS
30+
EDIT_PATIENTS
31+
VIEW_APPOINTMENTS
32+
EDIT_APPOINTMENTS
33+
VIEW_DASHBOARD
34+
}
35+
2536
model Patient {
26-
id String @id @default(cuid())
27-
firstName String
28-
lastName String
29-
email String? @unique
30-
phone String?
31-
birthDate DateTime?
32-
createdAt DateTime @default(now())
33-
updatedAt DateTime @updatedAt
37+
id String @id @default(cuid())
38+
firstName String
39+
lastName String
40+
email String? @unique
41+
phone String?
42+
birthDate DateTime?
43+
createdAt DateTime @default(now())
44+
updatedAt DateTime @updatedAt
3445
}

prisma/seed.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ async function main() {
3131
email,
3232
password: hash,
3333
role: Role.ADMIN,
34+
permissions: [
35+
"MANAGE_USERS",
36+
"MANAGE_ROLES",
37+
"VIEW_PATIENTS",
38+
"EDIT_PATIENTS",
39+
"VIEW_APPOINTMENTS",
40+
"EDIT_APPOINTMENTS",
41+
"VIEW_DASHBOARD"
42+
]
3443
},
3544
});
3645

src/controllers/auth.controller.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ export const login = async (req: Request, res: Response) => {
2525
}
2626

2727
// Sign JWT Token 'Ring'.
28-
const token = signToken({
29-
sub: user.id,
30-
email: user.email,
31-
role: user.role,
32-
});
28+
const token = signToken(
29+
{
30+
sub: user.id,
31+
email: user.email,
32+
role: user.role,
33+
permissions: user.permissions,
34+
}
35+
);
3336

3437
// Returned token and user info.
3538
return res.json({
@@ -49,7 +52,7 @@ export const login = async (req: Request, res: Response) => {
4952
// Register Controller.
5053
export const register = async (req: Request, res: Response) => {
5154
try {
52-
const { email, password, role } = req.body;
55+
const { email, password, role, permissions } = req.body;
5356

5457
// Check if email and password are provided.
5558
if (!email || !password) {
@@ -60,22 +63,23 @@ export const register = async (req: Request, res: Response) => {
6063
const existing = await findUserByEmail(email);
6164
if (existing) {
6265
return res.status(409).json({ message: 'Email already exists.' });
63-
}
66+
}
6467

6568
// Calling to hash the password
6669
const hashed = await hashPassword(password);
6770

6871
// Calling to create a new user with default role as RECEPTIONIST
6972
// if not provided in the body.
7073
// Later restrict this router to admin ONLY!!.
71-
const user = await createUser(email, hashed, role);
74+
const user = await createUser(email, hashed, role, permissions);
7275

7376
return res.status(201).json({
7477
message: 'User Created Successfully',
7578
user: {
7679
id: user.id,
7780
email: user.email,
78-
role: user.role
81+
role: user.role,
82+
permissions: user.permissions
7983
}
8084
});
8185
} catch (error) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const requirePermission = (...required: string[]) => {
2+
return (req: any, res: any, next: any) => {
3+
const userPermissions = req.user.permissions || [];
4+
5+
const hasAll = required.every(p => userPermissions.includes(p));
6+
7+
if(!hasAll){
8+
return res.status(403).json({ message: "Forbidden: Insufficient Permissions." });
9+
}
10+
11+
next();
12+
};
13+
};

src/routes/auth.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,23 @@ router.post('/login', login);
1111
// User must be logged in & have role ADMIN.
1212
router.post('/register', authenticate, authorize('ADMIN'), register);
1313

14+
/*
15+
16+
// PATIENTS
17+
router.post(
18+
'/patients',
19+
authenticate,
20+
requirePermission('EDIT_PATIENTS'),
21+
createPatient
22+
);
23+
24+
// USERS
25+
router.get(
26+
'/patients',
27+
authenticate,
28+
requirePermission('EDIT_PATIENTS'),
29+
createPatient
30+
);
31+
*/
32+
1433
export default router;

src/services/auth.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import bcrypt from 'bcrypt';
22
import { prisma } from '../config/prisma';
33
import { Role } from '../config/generated/enums'; // Imported generated type for Role
4+
import { Permission } from '../config/generated/enums';
45

56
const SALT_ROUNDS = 10;
67

@@ -15,12 +16,13 @@ export const comparePassword = async (password: string, hash: string) => {
1516
};
1617

1718
// Create a new User.
18-
export const createUser = async (email: string, password: string, role: Role) => {
19+
export const createUser = async (email: string, password: string, role: Role, permissions: Permission[]) => {
1920
return prisma.user.create({
2021
data: {
2122
email,
2223
password,
2324
role,
25+
permissions
2426
},
2527
});
2628
};

src/services/jwt.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface JWTPayload {
99
sub: string;
1010
email: string;
1111
role: string;
12+
permissions: string[];
1213
};
1314

1415
// Sign a new JWT Token.

0 commit comments

Comments
 (0)