Skip to content

Commit 8352c66

Browse files
authored
Merge pull request #8 from CS3219-AY2425S1/PEER-208-Lockout-Measure
PEER-208 Add Lockout measures
2 parents b5da177 + df46888 commit 8352c66

20 files changed

+987
-102
lines changed

backend/user/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ services:
2626
dockerfile: ./express.Dockerfile
2727
target: build
2828
ports:
29-
- "8001:8001"
29+
- "9000:8001"
3030
command: node dist/index.js
3131
depends_on:
3232
postgres:
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
CREATE TABLE IF NOT EXISTS "tableName" (
1+
CREATE TABLE IF NOT EXISTS "users" (
22
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3-
CONSTRAINT "tableName_id_unique" UNIQUE("id")
3+
"email" varchar(255) NOT NULL,
4+
"username" varchar(255) NOT NULL,
5+
"first_name" varchar(255) NOT NULL,
6+
"last_name" varchar(255) NOT NULL,
7+
"password" varchar(255) NOT NULL,
8+
CONSTRAINT "users_email_unique" UNIQUE("email"),
9+
CONSTRAINT "users_username_unique" UNIQUE("username")
410
);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE "users" ADD COLUMN "failed_attempts" smallint DEFAULT 0;--> statement-breakpoint
2+
ALTER TABLE "users" ADD COLUMN "unlock_time" timestamp (6) with time zone;

backend/user/drizzle/0001_clammy_grandmaster.sql

Lines changed: 0 additions & 12 deletions
This file was deleted.

backend/user/drizzle/meta/0000_snapshot.json

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"id": "bfb0f47d-1ebe-47d4-b979-8fe9d6305cd4",
2+
"id": "5dc3ebe6-ff0a-454d-b059-92180971dcfa",
33
"prevId": "00000000-0000-0000-0000-000000000000",
44
"version": "7",
55
"dialect": "postgresql",
66
"tables": {
7-
"public.tableName": {
8-
"name": "tableName",
7+
"public.users": {
8+
"name": "users",
99
"schema": "",
1010
"columns": {
1111
"id": {
@@ -14,17 +14,54 @@
1414
"primaryKey": true,
1515
"notNull": true,
1616
"default": "gen_random_uuid()"
17+
},
18+
"email": {
19+
"name": "email",
20+
"type": "varchar(255)",
21+
"primaryKey": false,
22+
"notNull": true
23+
},
24+
"username": {
25+
"name": "username",
26+
"type": "varchar(255)",
27+
"primaryKey": false,
28+
"notNull": true
29+
},
30+
"first_name": {
31+
"name": "first_name",
32+
"type": "varchar(255)",
33+
"primaryKey": false,
34+
"notNull": true
35+
},
36+
"last_name": {
37+
"name": "last_name",
38+
"type": "varchar(255)",
39+
"primaryKey": false,
40+
"notNull": true
41+
},
42+
"password": {
43+
"name": "password",
44+
"type": "varchar(255)",
45+
"primaryKey": false,
46+
"notNull": true
1747
}
1848
},
1949
"indexes": {},
2050
"foreignKeys": {},
2151
"compositePrimaryKeys": {},
2252
"uniqueConstraints": {
23-
"tableName_id_unique": {
24-
"name": "tableName_id_unique",
53+
"users_email_unique": {
54+
"name": "users_email_unique",
2555
"nullsNotDistinct": false,
2656
"columns": [
27-
"id"
57+
"email"
58+
]
59+
},
60+
"users_username_unique": {
61+
"name": "users_username_unique",
62+
"nullsNotDistinct": false,
63+
"columns": [
64+
"username"
2865
]
2966
}
3067
}

backend/user/drizzle/meta/0001_snapshot.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"id": "ecf4404b-9642-42c5-b6d6-deeaeaca7a77",
3-
"prevId": "bfb0f47d-1ebe-47d4-b979-8fe9d6305cd4",
2+
"id": "f5be5ec3-9d76-437e-a011-372a55170295",
3+
"prevId": "5dc3ebe6-ff0a-454d-b059-92180971dcfa",
44
"version": "7",
55
"dialect": "postgresql",
66
"tables": {
@@ -44,6 +44,19 @@
4444
"type": "varchar(255)",
4545
"primaryKey": false,
4646
"notNull": true
47+
},
48+
"failed_attempts": {
49+
"name": "failed_attempts",
50+
"type": "smallint",
51+
"primaryKey": false,
52+
"notNull": false,
53+
"default": 0
54+
},
55+
"unlock_time": {
56+
"name": "unlock_time",
57+
"type": "timestamp (6) with time zone",
58+
"primaryKey": false,
59+
"notNull": false
4760
}
4861
},
4962
"indexes": {},

backend/user/drizzle/meta/_journal.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
{
66
"idx": 0,
77
"version": "7",
8-
"when": 1726382290778,
8+
"when": 1726749267171,
99
"tag": "0000_initial_schema",
1010
"breakpoints": true
1111
},
1212
{
1313
"idx": 1,
1414
"version": "7",
15-
"when": 1726678203279,
16-
"tag": "0001_clammy_grandmaster",
15+
"when": 1726811697689,
16+
"tag": "0001_add_lockout_schema",
1717
"breakpoints": true
1818
}
1919
]

backend/user/package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"fmt": "prettier --config .prettierrc src --write",
1010
"db:generate": "drizzle-kit generate",
1111
"db:migrate": "tsx drizzle.migrate.mts",
12+
"db:inspect": "drizzle-kit studio",
1213
"test": "echo \"Error: no test specified\" && exit 1"
1314
},
1415
"keywords": [],
@@ -19,19 +20,26 @@
1920
"bcrypt": "^5.1.1",
2021
"drizzle-orm": "^0.33.0",
2122
"express": "^4.21.0",
23+
"express-rate-limit": "^7.4.0",
24+
"helmet": "^7.1.0",
25+
"http-status-codes": "^2.3.0",
2226
"jsonwebtoken": "^9.0.2",
2327
"pino": "^9.4.0",
2428
"pino-http": "^10.3.0",
2529
"postgres": "^3.4.4"
2630
},
2731
"devDependencies": {
32+
"@swc/core": "^1.7.26",
33+
"@swc/helpers": "^0.5.13",
2834
"@types/bcrypt": "^5.0.2",
2935
"@types/express": "^4.17.21",
3036
"@types/jsonwebtoken": "^9.0.6",
3137
"@types/node": "^22.5.5",
3238
"drizzle-kit": "^0.24.2",
3339
"nodemon": "^3.1.4",
3440
"pino-pretty": "^11.2.2",
35-
"ts-node": "^10.9.2"
41+
"regenerator-runtime": "^0.14.1",
42+
"ts-node": "^10.9.2",
43+
"tsconfig-paths": "^4.2.0"
3644
}
3745
}

backend/user/src/controllers/auth/auth-controller.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Request, Response } from 'express';
2+
import { StatusCodes } from 'http-status-codes';
3+
4+
import { loginService } from '@/services/auth';
5+
import type { ILoginPayload } from '@/services/auth/types';
6+
7+
export async function login(req: Request, res: Response) {
8+
const { username, password }: Partial<ILoginPayload> = req.body;
9+
if (!username || !password) {
10+
return res.status(StatusCodes.UNPROCESSABLE_ENTITY).json('Malformed Request');
11+
}
12+
const { code, data, error } = await loginService({ username, password });
13+
if (error || code !== StatusCodes.OK || !data) {
14+
const sanitizedErr = error?.message ?? 'An error occurred.';
15+
return res.status(code).json(sanitizedErr);
16+
}
17+
return res
18+
.status(StatusCodes.OK)
19+
.cookie('jwtToken', data.cookie, { httpOnly: true })
20+
.json(data.user);
21+
}
22+
23+
export async function logout(_req: Request, res: Response) {
24+
return res
25+
.clearCookie('jwtToken', {
26+
secure: true,
27+
sameSite: 'none',
28+
})
29+
.status(StatusCodes.OK)
30+
.json('User has been logged out.');
31+
}

0 commit comments

Comments
 (0)