From 712ecfa12f4ffbcedbd7b1553b78e250cf0cbcee Mon Sep 17 00:00:00 2001 From: "alec.schmidt" <alec.schmidt@etu.hesge.ch> Date: Fri, 9 Jun 2023 12:52:26 +0200 Subject: [PATCH] admin perms fix and routes more safe --- API/db/app.db | Bin 36864 -> 36864 bytes API/src/database/Database.ts | 17 +-- API/src/express/Server.ts | 1 - API/src/routes/BaseRoutes.ts | 97 ++++++++++++------ frontend/src/app/admin/admin.component.ts | 15 +-- frontend/src/app/app.component.html | 2 +- frontend/src/app/app.component.ts | 6 ++ .../app/services/authentication.service.ts | 6 +- frontend/src/app/services/users.service.ts | 1 + 9 files changed, 95 insertions(+), 50 deletions(-) diff --git a/API/db/app.db b/API/db/app.db index 918b6e267f795f9e97beb6aa37d825f69d9d17ce..64c90b46e77bf7c6bb80320c2b8304adf8bf2b65 100644 GIT binary patch delta 240 zcmZozz|^pSX@WGP^F$eEM(2$QOZa)1`0q3Df8@W<|A>F{W&wf4{B{fs40f`j9C`UA zi7C06d4@*DCJYP=+M=T3C8@<FAe@$%Q=H1cz#zmcz|YDcEy@v=nU|cJR{~Nx`J=or z7Zd**2L3nvKltBo=2JMquf)K>pae6Ihk=1X6l4lFH?uS&R44!DANqgzB{(@5nZ+4P zi&Kk=`FR-_7#R6aFz}z?-@I8-VHLlSsHieXpZnpJ`RPT81sR#ClTY|NGoIcoXmE(1 WixVWrsm&bCIQg8v`sV-fj0ONZTt@!@ delta 215 zcmZozz|^pSX@WGP@<bVDM&*qOOZa&h`9Cu7f8@W)e~f?SW&wf4{C18hxtV#P1u2Oo zsfI?zCTWQ|#S9D#+R~z;VVQZ!sd*)a3=9l1vht$t`8g?>RaFJSr3I-)Mh1pPB}JvF zlRwG}b20M&VBr73|AYVCW<G@z{7NNAW->4^h=C1aU|`^d8Ns>vhyEXa2@Xz1W^u;S z;?$yIPF@BE21fo94E!hf&u<nq*uy{hgugT6_sxO^C-_;knWGsepYvDW{6C)2002<) BL#qG) diff --git a/API/src/database/Database.ts b/API/src/database/Database.ts index 42c744f..5bb06e6 100644 --- a/API/src/database/Database.ts +++ b/API/src/database/Database.ts @@ -33,7 +33,6 @@ export type Answer_t = { } export type Category_t = { - id: number; category: string; }; @@ -59,7 +58,7 @@ class DBHandler { if (usernameInDB.length == 0) return false; - return (usernameInDB[0].username == username); + return (usernameInDB[0].username === username); } async verifyQuestionExistence(question:string) : Promise<boolean> { @@ -76,6 +75,12 @@ class DBHandler { return (password[0].password == user.password); } + async getUserById(id: number) : Promise<User_t> { + const query = "SELECT username, password, admin FROM users WHERE id="+id; + const user = await asyncdb.get<User_t>(query); + return user; + } + async getUsers(res:express.Response) { const query = "SELECT id, username, admin FROM users"; asyncdb.all(query) @@ -92,7 +97,7 @@ class DBHandler { a.admin = false; const query = "INSERT INTO users (username, password, admin) \ - VALUES ('" + a.username + "','" + a.password + "','" + a.admin +"')"; + VALUES ('" + a.username + "','" + a.password + "'," + a.admin +")"; asyncdb.all(query) .then( () => res.status(StatusCodes.OK)) @@ -103,7 +108,7 @@ class DBHandler { const a = req.body as User_t; const request = "UPDATE users \ - SET username = '"+ a.username + "', admin = '" + a.admin + "' WHERE id = " + req.params.id; + SET username = '"+ a.username + "', admin =" + a.admin + " WHERE id = " + req.params.id; asyncdb.all(request) .then(() => res.status(StatusCodes.OK).end()) @@ -158,7 +163,7 @@ class DBHandler { let questions = await asyncdb.all(query); - let ans = await asyncdb.all(query_answers); + // let ans = await asyncdb.all(query_answers); // let payload = { // QUESTIONS: questions, @@ -205,7 +210,7 @@ class DBHandler { async deleteCategory(req:express.Request, res:express.Response) { const a = req.body as Category_t; - const request = "DELETE FROM category WHERE id = " + a.id; + const request = "DELETE FROM category WHERE title = '" + a.category + "'"; asyncdb.all(request) .then(() => res.status(StatusCodes.OK).end()) .catch(e => console.log(e)); diff --git a/API/src/express/Server.ts b/API/src/express/Server.ts index 208e728..877b0c3 100644 --- a/API/src/express/Server.ts +++ b/API/src/express/Server.ts @@ -10,7 +10,6 @@ import multer from 'multer'; import Config from '../config/Config'; import BaseRoutes from '../routes/BaseRoutes'; import ServerIO from '../socket.io/ServerIO'; -import DBHandler from '../database/Database'; export class Server { private readonly backend: Express; diff --git a/API/src/routes/BaseRoutes.ts b/API/src/routes/BaseRoutes.ts index bbff290..a5cfef4 100644 --- a/API/src/routes/BaseRoutes.ts +++ b/API/src/routes/BaseRoutes.ts @@ -2,8 +2,8 @@ import express from 'express'; import { StatusCodes } from 'http-status-codes'; import DBHandler from '../database/Database'; import { User_t, Question_t } from '../database/Database'; -import { Jwt } from 'jsonwebtoken'; -import { userInfo } from 'os'; +import { param } from 'express-validator'; + const ROUTE:string = '/API/v1'; @@ -21,8 +21,8 @@ function generateToken(user: User_t) { return jwt.sign(payload, process.env.TOKEN_SECRET); } -function isAdmin(token: string): Boolean { - let adminToken: Boolean; +function isAdmin(token: string): boolean { + let adminToken: boolean; if (token == null) return false; jwt.verify(token, process.env.TOKEN_SECRET, (err:any, user:User_t) => { @@ -41,7 +41,6 @@ function tokenDecode(req: express.Request): User_t { const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1] jwt.verify(token, process.env.TOKEN_SECRET, (err:any, user:User_t) => { - // console.log(err); if (err) user_data = undefined; else @@ -55,7 +54,11 @@ function tokenDecode(req: express.Request): User_t { router.post(ROUTE+'/user', (req: express.Request, res: express.Response) => { const a = req.body as User_t; - + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1] + + if (a.admin && !isAdmin(token)) + return res.status(StatusCodes.UNAUTHORIZED).end(); + if (a.username === undefined || a.password === undefined) { @@ -87,36 +90,48 @@ router.get(ROUTE+'/user', (req: express.Request, res: express.Response) => { router.patch(ROUTE+'/user/:id', (req: express.Request, res: express.Response) => { const a = req.body as User_t; + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1] + const id: number = +req.params.id; - // if (a.username === undefined || - // a.password === undefined || - // a.type === undefined) { - - // res.statusMessage = "invalid JSON"; - // res.status(StatusCodes.BAD_REQUEST).end(); - // return; - // } + if (a.admin && !isAdmin(token)) + res.status(StatusCodes.UNAUTHORIZED).end(); + + if (a.username === undefined || + a.admin === undefined) { - console.log(a); + res.statusMessage = "invalid JSON"; + res.status(StatusCodes.BAD_REQUEST).end(); + return; + } - if (a.admin === true) - if (!isAdmin(req.headers['authorization'] && req.headers['authorization'].split(' ')[1])) - res.status(StatusCodes.UNAUTHORIZED).end(); - const user = tokenDecode(req); - - if (a.username != user.username) - DBHandler.verifyUsernameExistence(a.username) - .then(exists => { - if(exists) - return res.status(StatusCodes.CONFLICT).end(); + DBHandler.getUserById(id).then(user => { + if (!isAdmin(token)) + user = tokenDecode(req); + + if (a.username !== user.username) + DBHandler.verifyUsernameExistence(a.username) + .then(exists => { + if(exists) + return res.status(StatusCodes.CONFLICT).end(); + }) - DBHandler.updateUser(req, res); - }) - + DBHandler.updateUser(req, res); + }) }); router.delete(ROUTE+"/user/:id", (req: express.Request, res: express.Response) => { + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1]; + const id: number = +req.params.id; + + if(!isAdmin(token)) { + DBHandler.getUserById(id) + .then(user => { + if (user.username !== tokenDecode(req).username) + return res.status(StatusCodes.UNAUTHORIZED).end(); + }) + } + DBHandler.deleteUser(req, res); }) @@ -127,11 +142,10 @@ router.post(ROUTE+'/question', (req: express.Request, res: express.Response) => if (a.question === undefined || a.category === undefined) { - res.statusCode = 400; res.statusMessage = "invalid JSON"; - res.end(); - return; + return res.status(StatusCodes.BAD_REQUEST).end(); } + DBHandler.verifyQuestionExistence(a.question) .then(exists => { @@ -151,10 +165,19 @@ router.get(ROUTE+'/question', (req: express.Request, res: express.Response) => { }) router.patch(ROUTE+'/question/:id', (req: express.Request, res: express.Response) => { + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1]; + + if(!isAdmin(token)) + return res.status(StatusCodes.UNAUTHORIZED).end() + DBHandler.updateQuestion(req, res); }) router.delete(ROUTE+'/question/:id', (req: express.Request, res: express.Response) => { + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1]; + if(!isAdmin(token)) + return res.status(StatusCodes.UNAUTHORIZED).end(); + DBHandler.deleteQuestion(req, res); }) @@ -165,12 +188,20 @@ router.get(ROUTE+'/answer/:questionId', (req: express.Request, res: express.Resp }) router.post(ROUTE+'/answer', (req: express.Request, res: express.Response) => { + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1]; + if(!isAdmin(token)) + return res.status(StatusCodes.UNAUTHORIZED).end(); + DBHandler.postAnwsers(req, res); }) // CATEGORIES CRUD router.post(ROUTE+'/category', (req: express.Request, res: express.Response) => { + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1]; + if(!isAdmin(token)) + return res.status(StatusCodes.UNAUTHORIZED).end(); + DBHandler.postCategory(req, res); }) @@ -179,6 +210,10 @@ router.get(ROUTE+'/category', (req: express.Request, res: express.Response) => { }) router.delete(ROUTE+"/category", (req: express.Request, res: express.Response) => { + const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1]; + if(!isAdmin(token)) + return res.status(StatusCodes.UNAUTHORIZED).end(); + DBHandler.deleteCategory(req, res); }) diff --git a/frontend/src/app/admin/admin.component.ts b/frontend/src/app/admin/admin.component.ts index 33e6297..a09535b 100644 --- a/frontend/src/app/admin/admin.component.ts +++ b/frontend/src/app/admin/admin.component.ts @@ -4,6 +4,8 @@ import { Answer, QandA, Question, User } from '../Types/types'; import { UsersService } from '../services/users.service'; import { QuestionsService } from '../services/questions.service'; import { Subscription } from 'rxjs'; +import { AuthenticationService } from '../services/authentication.service'; +import { Router } from '@angular/router'; @Component({ selector: 'app-admin', @@ -15,8 +17,6 @@ export class AdminComponent implements OnInit { public users!: User[]; public questions!: Question[]; public answers!: Answer[]; - private usersSub!: Subscription; - private questionsSub!: Subscription; userToEdit!: User; modalUpdate: boolean = false; @@ -24,19 +24,22 @@ export class AdminComponent implements OnInit { modalQuestionCreate: boolean = false; constructor( - private http: HttpClient, + private router: Router, private userController: UsersService, - private questionController: QuestionsService) { } + private questionController: QuestionsService, + private auth: AuthenticationService) { } ngOnInit(): void { + if(!this.auth.isAdmin()) + this.router.navigateByUrl("/"); - this.usersSub = this.userController.users.subscribe(userList => { + this.userController.users.subscribe(userList => { this.users = userList; }); this.userController.fetchUsers(); - this.questionsSub = this.questionController.questions.subscribe(questionList => { + this.questionController.questions.subscribe(questionList => { this.questions = questionList; }) diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index ae3f9f4..7acbaf1 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -4,7 +4,7 @@ <h2 class="mr-6 border-2 border-transparent font-bold">Navbar</h2> <div> <button class="mr-4 border-2 border-transparent hover:border-solid hover:border-white hover:border-2 hover:rounded-lg px-2" (click)="router.navigateByUrl('/');">Home</button> - <button class="border-2 border-transparent hover:border-solid hover:border-white hover:border-2 hover:rounded-lg px-2" *ngIf="auth.isAdmin()" (click)="router.navigateByUrl('/admin')">Dashboard</button> + <button class="border-2 border-transparent hover:border-solid hover:border-white hover:border-2 hover:rounded-lg px-2" *ngIf="this.auth.isAdmin()" (click)="router.navigateByUrl('/admin')">Dashboard</button> </div> </div> <div class="flex flex-row"> diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 0e30aeb..312da32 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -13,4 +13,10 @@ export class AppComponent { constructor( public router: Router, public auth: AuthenticationService) {} + + + dashboardButton(): boolean { + console.log(this.auth.isAdmin()) + return this.auth.isAdmin(); + } } diff --git a/frontend/src/app/services/authentication.service.ts b/frontend/src/app/services/authentication.service.ts index ab540bc..e58e3f7 100644 --- a/frontend/src/app/services/authentication.service.ts +++ b/frontend/src/app/services/authentication.service.ts @@ -20,10 +20,6 @@ export class AuthenticationService { username, password } - const headers = new Headers({ - 'Content-Type': 'application/json' - }); - this.http.post('http://0.0.0.0:30992/API/v1/login', body, {observe: 'response'}) .subscribe(res => { if(res.ok) { @@ -54,7 +50,7 @@ export class AuthenticationService { } } - isAdmin(): Boolean { + isAdmin(): boolean { const user = this.getDecodedAccessToken() as User; if (user === null) diff --git a/frontend/src/app/services/users.service.ts b/frontend/src/app/services/users.service.ts index ec3ff59..7ceb9a3 100644 --- a/frontend/src/app/services/users.service.ts +++ b/frontend/src/app/services/users.service.ts @@ -24,6 +24,7 @@ export class UsersService { } addUser(user: User) { + this.http.post<User>(ROUTE, user).subscribe(() => { this.auth.login(user.username, user.password); }); -- GitLab