Skip to content

Commit 97f8d51

Browse files
Merge pull request #632 from SELab-2/400-backend---application---fix-student-teacher-account-overlap
[Backend] - Implementend User Acces Rights
2 parents a9d0fa3 + fd20fff commit 97f8d51

File tree

156 files changed

+1004
-977
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

156 files changed

+1004
-977
lines changed

backend/api/openapi.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,6 +2520,7 @@ components:
25202520
required:
25212521
- email
25222522
- password
2523+
- userType
25232524
additionalProperties: false
25242525
properties:
25252526
email:
@@ -2529,6 +2530,11 @@ components:
25292530
password:
25302531
type: string
25312532
description: User's password.
2533+
userType:
2534+
type: string
2535+
enum: [student, teacher]
2536+
default: student
2537+
description: User's type in the system.
25322538

25332539
RegisterResponse:
25342540
type: object

backend/src/application/auth.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import bcrypt from "bcryptjs";
22
import jwt from "jsonwebtoken";
33
import { AuthenticationTokenPayload, TokenResponse } from "./types";
4-
import { User } from "../core/entities/user";
4+
import { User, UserType } from "../core/entities/user";
55
import { GetUser, GetUserInput } from "../core/services/user";
66

77
// TODO - Consider allowing only one active token per user (probably not)
@@ -33,7 +33,12 @@ export class AuthenticationManager {
3333
this.refreshExpiresIn = refreshExpiresIn;
3434
}
3535

36-
async authenticate(email: string, password: string, refreshToken?: string): Promise<TokenResponse | null> {
36+
async authenticate(
37+
email: string,
38+
password: string,
39+
userType?: UserType,
40+
refreshToken?: string,
41+
): Promise<TokenResponse | null> {
3742
if (refreshToken) {
3843
const refreshResult = this.refreshAccessToken(refreshToken);
3944
if (refreshResult) {
@@ -43,14 +48,13 @@ export class AuthenticationManager {
4348
const input: GetUserInput = { email: email };
4449
let user = undefined;
4550
try {
46-
user = (await this.getUserService.execute(input)) as User;
51+
user = (await this.getUserService.execute("", input)) as User;
4752
} catch (e) {
4853
return null;
4954
}
50-
if (user && (await bcrypt.compare(password, user.passwordHash))) {
55+
if (user && userType && user.userType === userType && (await bcrypt.compare(password, user.passwordHash))) {
5156
return this.generateTokens(user.id!);
5257
}
53-
console.log("not supposed to be here");
5458
return null;
5559
}
5660

backend/src/application/controller.ts

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createErrorHandler, defaultResponder, ResponderFunction } from "./helpersExpress";
2-
import { ApiError, Request, Response, ErrorCode } from "./types";
2+
import { ApiError, Request, Response, ErrorCode, AuthenticatedRequestBody } from "./types";
33
import { logger } from "../config/logger";
44
import { Service, Services } from "../config/service";
55

@@ -60,6 +60,7 @@ export abstract class Controller {
6060
data: T,
6161
statusCode: number,
6262
operationName: string,
63+
userId: string,
6364
): Promise<Response> {
6465
if (!service) {
6566
this.logger.warn(`${operationName} operation not implemented`);
@@ -69,7 +70,7 @@ export abstract class Controller {
6970
});
7071
}
7172
this.logger.info(`Executing service: ${operationName}`, { statusCode });
72-
const body = await service.execute(data);
73+
const body = await service.execute(userId, data);
7374
return this.respond(statusCode, body);
7475
}
7576

@@ -80,7 +81,13 @@ export abstract class Controller {
8081
* @returns Response with status 200 and entity data
8182
*/
8283
public async getOne<T>(req: Request, data: T): Promise<Response> {
83-
return this._executeService(this.services.get, data, 200, "Get");
84+
return this._executeService(
85+
this.services.get,
86+
data,
87+
200,
88+
"Get",
89+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
90+
);
8491
}
8592

8693
/**
@@ -90,7 +97,13 @@ export abstract class Controller {
9097
* @returns Response with status 200 and list of entities
9198
*/
9299
public async getAll<T>(req: Request, data: T): Promise<Response> {
93-
return this._executeService(this.services.getAll, data, 200, "GetAll");
100+
return this._executeService(
101+
this.services.getAll,
102+
data,
103+
200,
104+
"GetAll",
105+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
106+
);
94107
}
95108

96109
/**
@@ -101,7 +114,13 @@ export abstract class Controller {
101114
* @returns Response with status 200 and list of child entities
102115
*/
103116
public async getChildren<T>(req: Request, data: T, service: Service<T>): Promise<Response> {
104-
return this._executeService(service, data, 200, "GetChildren");
117+
return this._executeService(
118+
service,
119+
data,
120+
200,
121+
"GetChildren",
122+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
123+
);
105124
}
106125

107126
/**
@@ -112,7 +131,13 @@ export abstract class Controller {
112131
* @returns Response with status 201 and created child entity data
113132
*/
114133
public async addChild<T>(req: Request, data: T, service: Service<T>): Promise<Response> {
115-
return this._executeService(service, data, 201, "AddChild");
134+
return this._executeService(
135+
service,
136+
data,
137+
201,
138+
"AddChild",
139+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
140+
);
116141
}
117142

118143
/**
@@ -123,7 +148,13 @@ export abstract class Controller {
123148
* @returns Response with status 204 (No Content)
124149
*/
125150
public async removeChild<T>(req: Request, data: T, service: Service<T>): Promise<Response> {
126-
return this._executeService(service, data, 204, "RemoveChild");
151+
return this._executeService(
152+
service,
153+
data,
154+
204,
155+
"RemoveChild",
156+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
157+
);
127158
}
128159

129160
/**
@@ -133,7 +164,13 @@ export abstract class Controller {
133164
* @returns Response with status 200 and updated entity data
134165
*/
135166
public async update<T>(req: Request, data: T): Promise<Response> {
136-
return this._executeService(this.services.update, data, 204, "Update");
167+
return this._executeService(
168+
this.services.update,
169+
data,
170+
204,
171+
"Update",
172+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
173+
);
137174
}
138175

139176
/**
@@ -143,7 +180,13 @@ export abstract class Controller {
143180
* @returns Response with status 204 (No Content)
144181
*/
145182
public async delete<T>(req: Request, data: T): Promise<Response> {
146-
return this._executeService(this.services.remove, data, 204, "Delete");
183+
return this._executeService(
184+
this.services.remove,
185+
data,
186+
204,
187+
"Delete",
188+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
189+
);
147190
}
148191

149192
/**
@@ -153,6 +196,12 @@ export abstract class Controller {
153196
* @returns Response with status 201 and created entity data
154197
*/
155198
public async create<T>(req: Request, data: T): Promise<Response> {
156-
return this._executeService(this.services.create, data, 201, "Create");
199+
return this._executeService(
200+
this.services.create,
201+
data,
202+
201,
203+
"Create",
204+
(req.body as AuthenticatedRequestBody).authenticatedUserId,
205+
);
157206
}
158207
}

backend/src/application/middleware/loginMiddleware.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Request as ExpressRequest, Response as ExpressResponse } from "express";
2+
import { UserType } from "../../core/entities/user";
23
import { AuthenticationManager } from "../auth";
34
import { defaultErrorHandler, defaultResponder, responseToExpress } from "../helpersExpress";
45
import { ErrorCode } from "../types";
@@ -21,18 +22,23 @@ export function loginMiddleware(
2122
return;
2223
}
2324

24-
const { email, password, refreshToken } = req.body || {};
25-
if (!((email && password) || (!email && !password && refreshToken))) {
25+
const { email, password, userType, refreshToken } = req.body || {};
26+
if (!((email && password && userType) || (!email && !password && !userType && refreshToken))) {
2627
const response = defaultErrorHandler({
2728
code: ErrorCode.BAD_REQUEST,
28-
message: "Email and password or refresh token are required",
29+
message: "Email, password and userType or refresh token are required",
2930
});
3031
responseToExpress(response, res);
3132
return;
3233
}
3334

3435
try {
35-
const tokens = await authManager.authenticate(email || "", password || "", refreshToken);
36+
const tokens = await authManager.authenticate(
37+
email || "",
38+
password || "",
39+
userType as UserType,
40+
refreshToken,
41+
);
3642
if (!tokens) {
3743
const response = defaultErrorHandler({
3844
code: ErrorCode.UNAUTHORIZED,

backend/src/application/resources/taskResource.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export function taskRoutes(
7979
controller,
8080
extractor: extractors.getTask,
8181
handler: (req, data) => controller.getOne(req, data),
82+
middleware,
8283
},
8384
{
8485
app,
@@ -87,6 +88,7 @@ export function taskRoutes(
8788
controller,
8889
extractor: extractors.getTask,
8990
handler: (req, data) => controller.delete(req, data),
91+
middleware,
9092
},
9193
{
9294
app,

backend/src/application/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ export interface TokenResponse {
8484
refreshToken: string;
8585
}
8686

87+
export interface AuthenticatedRequestBody {
88+
authenticatedUserId: string;
89+
[key: string]: string;
90+
}
91+
8792
/* ************* Path/Routing Types ************* */
8893

8994
/**

backend/src/config/service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//TODO if application layer always asks for an object in return to we need to specify ReturnType?
22
export interface Service<T> {
3-
execute: (input: T) => Promise<object>;
3+
execute: (userId: string, input: T) => Promise<object>;
44
}
55

66
//TODO can we abstract the params type in services to for a Params as abstracted class or interface?

0 commit comments

Comments
 (0)