From 25606bd438d0f0d99580b730b58d0cdcace6d9d2 Mon Sep 17 00:00:00 2001 From: Jesper <> Date: Fri, 4 Oct 2024 11:49:39 +0200 Subject: [PATCH] idk --- backend/src/User/Controller.ts | 26 ++++++++++-- backend/src/admin/Controller.ts | 42 ++++++++++++++++++- backend/src/bun.lockb | Bin 18946 -> 19346 bytes backend/src/index.ts | 34 ++++++++++++++- backend/src/interfaces/user_interface.ts | 7 ++++ backend/src/package.json | 1 + backend/src/password/Controller.ts | 44 +++++++++++++++++++ backend/src/utils/groups.ts | 0 backend/src/utils/send_mail.ts | 51 +++++++++++++++++++++++ 9 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 backend/src/interfaces/user_interface.ts create mode 100644 backend/src/password/Controller.ts create mode 100644 backend/src/utils/groups.ts create mode 100644 backend/src/utils/send_mail.ts diff --git a/backend/src/User/Controller.ts b/backend/src/User/Controller.ts index 081c107..193fdf1 100644 --- a/backend/src/User/Controller.ts +++ b/backend/src/User/Controller.ts @@ -6,21 +6,39 @@ import { authenticator } from 'otplib'; const user = new User(); const userController = new Elysia() + .get('/', ({user}) => { + return user + }) .post('/login', async ({ body }) => { - const msg = db.query(`select id, password, otp from users WHERE username = ?;`) + const msg = db.query(`select id, password, administrator, otp from users WHERE username = ?;`) .get(body.username) console.log(msg) if(msg == null) return new Response("Invalid username or password", { status: 401 }) const validPassword = await Bun.password.verify(body.password, msg.password) if (!validPassword) return new Response("Invalid username or password", { status: 401 }) // if(!authenticator.check(body.otp, msg.otp)) return new Response("Invalid OTP code", { status: 401 }) + if(msg.administrator == 1) {return new Response(await user.createToken(msg.id), {status: 418})} return await user.createToken(msg.id) }) - .post('/register', async({body}) => { - return await Bun.password.hash(body.password); - }) + // .post('/register', async({body}) => { + // return await Bun.password.hash(body.password); + // }) .get('/otp', () => { return authenticator.generateSecret(); }) + .put('/password', ({user}, body) => { + const oldPassword = body.oldPassword; + const newPassword = body.newPassword; + if (oldPassword == "" || newPassword == "") { + {return new Response("Bad password", {status: 400})} + } + + const msg = db.query(`select password from users WHERE id = ?;`).get(user.id); + if(!Bun.password.verify(oldPassword, msg.password)) { + {return new Response("Bad old password", {status: 400})} + } + const newPasswordHash = Bun.password.hash(body.newPassword) + const msg = db.query(`update users set password = ? WHERE id = ?;`).run(newPassword. user.id); + }) export default userController diff --git a/backend/src/admin/Controller.ts b/backend/src/admin/Controller.ts index 732fc7c..af2d949 100644 --- a/backend/src/admin/Controller.ts +++ b/backend/src/admin/Controller.ts @@ -1,10 +1,48 @@ import { Elysia } from 'elysia' +import db from '../Database' +import { authenticator } from 'otplib'; const adminController = new Elysia() .get('/', () => "admin endpoint") -.post('/register', async({body}) => { - return await Bun.password.hash(body.password); +.get('/users', () => { + const users = db.query(`SELECT id, username, name FROM users;`) + .all(); + return users +}) +.post('/user/group/:groupId', ({params: {groupId}, body}) => { + db.query(`INSERT INTO userGroups (UserID, GroupID) VALUES (?, ?);`).run(groupId, body.userId); + return "Added user to group" +}) +.delete('/user/:userId', ({params: {userId}}) => { + db.query(`DELETE FROM users WHERE id = ?;`).run(userId); + "deleted user" +}) +.post('/register', async({body}) => { + const password = await Bun.password.hash(body.password); + const otp = authenticator.generateSecret(); + const res = db.query(`INSERT INTO users(username, password, name, otp) VALUES (?, ?, ?, ?);`).run(body.username, password, body.name, otp); + return {id: res.lastInsertRowid, username: body.username, name: body.name, otp: otp} +}) +.get('/group', () => { + const groups = db.query(`SELECT GroupID, GroupName FROM groups;`) + .all(); + return groups +}) +.post('/group', ({body}) => { + const res = db.query(`INSERT INTO groups (GroupName) VALUES (?);`).run(body.name); + return {GroupID: res.lastInsertRowid, GroupName: body.name} +}) +.delete('/group/:groupId', ({ params: { groupId }}) => { + db.query(`DELETE FROM groups WHERE GroupID = ?;`).run(groupId); + + "deleted group" +}) +.delete('/password/:passwordId', ({params: {passwordId}}) => { + db.query(`DELETE FROM passwords WHERE id = ?;`).run(passwordId); + + "deleted password" }) + export default adminController \ No newline at end of file diff --git a/backend/src/bun.lockb b/backend/src/bun.lockb index c1a978ed5fedaf1dc2cd1bbbe584e8a92f3b768d..d271ee938be0a9d096191a200d28bc75c963c7d3 100755 GIT binary patch delta 2885 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 "Hello") +.derive(async ({ headers }) => { + const auth = headers['authorization'] + if (!auth) { + return {} + } + const bearer = auth?.startsWith('Bearer ') ? auth.slice(7) : null + + + const msg = db.query(`select users.id, users.name, users.administrator from users JOIN tokens ON users.id = tokens.user_id WHERE tokens.token = ?;`) + .get(bearer) + console.log(msg) + if(msg == null) { + throw new Response("Invalid token", { status: 401 }) + return {} + } + + const user: User = { + id: msg.id, + username: msg.username, + name: msg.name, + admin: msg.administrator + } + return { user } + + // return user here instead of bearer +}) +.get('/', ({ user }) => user) .group('/user', (app) => app.use(userController)) .group('/admin', (app) => app.use(adminController)) +.group('/password', (app) => app.use(passwordController)) +.use(cors()) .listen(3000) \ No newline at end of file diff --git a/backend/src/interfaces/user_interface.ts b/backend/src/interfaces/user_interface.ts new file mode 100644 index 0000000..b77ccb5 --- /dev/null +++ b/backend/src/interfaces/user_interface.ts @@ -0,0 +1,7 @@ +export type User = { + id: number, + name: string, + username: string, + password?: string, + admin: boolean, +} \ No newline at end of file diff --git a/backend/src/package.json b/backend/src/package.json index fee179a..8c1c74a 100644 --- a/backend/src/package.json +++ b/backend/src/package.json @@ -9,6 +9,7 @@ "typescript": "^5.0.0" }, "dependencies": { + "@elysiajs/cors": "^1.1.1", "elysia": "^1.1.12", "kysely": "^0.27.4", "nanoid": "^5.0.7", diff --git a/backend/src/password/Controller.ts b/backend/src/password/Controller.ts new file mode 100644 index 0000000..127f10c --- /dev/null +++ b/backend/src/password/Controller.ts @@ -0,0 +1,44 @@ +import { Elysia } from 'elysia' +import db from '../Database' + + +const passwordController = new Elysia() +.get('/', ({ user }) => { + const userGroup = db.query(`SELECT groups.GroupId, groups.GroupName FROM groups JOIN userGroups ON groups.GroupID = userGroups.GroupID JOIN users ON userGroups.UserID = users.id WHERE users.id = ?;`) + .all(user.id); + return userGroup +}) +.get('/:groupId', ({params: {groupId}}) => { + // make sure user has access to group + const passwords = db.query(`SELECT id, name, password, created_by, created_at from passwords WHERE group_id = ?;`) + .all(groupId); + console.log(passwords) + return passwords +}) +.post('/:groupId', async({params: {groupId}, body, user}) => { + const passName = body.name; + const password = body.password + + if (!passName || passName.trim() === "") { + return new Response("Name must be defined", { status: 400 }); + } + + const userGroup = db.query(`SELECT 1 FROM userGroups WHERE userID = ? AND groupID = ?;`) + .get(user.id, groupId); + + if (!userGroup) { + return new Response("Forbidden: You do not have access to this group", { status: 403 }); + } + + db.query(`INSERT INTO passwords (name, password, group_id, created_by) VALUES (?, ?, ?, ?);`).run(body.name, body.password, groupId, user.name); + + + return new Response("Password created successfully", { status: 201 }); +}) +.delete('/:passwordId', ({params: {passwordId}}) => { + db.query(`DELETE FROM users WHERE id = ?;`).run(userId); + + "deleted password" +}) + +export default passwordController \ No newline at end of file diff --git a/backend/src/utils/groups.ts b/backend/src/utils/groups.ts new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/utils/send_mail.ts b/backend/src/utils/send_mail.ts new file mode 100644 index 0000000..af3da9f --- /dev/null +++ b/backend/src/utils/send_mail.ts @@ -0,0 +1,51 @@ +import { SmtpClient } from 'smtp' +import config from "../email_config.json" assert { type: "json" }; + +const client = new SmtpClient(); + +export async function SendVerificationMail(userEmail: string, name: string, companyName: string, verificationToken: string) { + try { + await client.connect({ + hostname: config.smtp_server, + port: config.smtp_port, + username: config.smtp_username, + password: config.smtp_password, + }); + + await client.send({ + from: "PasswordBox ", + to: userEmail, + subject: "=?utf-8?B?QmVrcsOmZnQgZGluIGUtbWFpbC1hZHJlc3Nl?=", + content: `Kære ${name},

Velkommen til vores interne password manager PasswordBox. For at fuldføre oprettelsesprocessen skal du bekræfte din e-mail-adresse ved at klikke på nedenstående link:

https://passwordbox.dk/verify?token=${verificationToken}

Hvis du ikke vil tilmeldes vores tjeneste, kan du blot ignorere denne e-mail.

Med venlig hilsen,
PasswordBox

Denne e-mail kan ikke besvares`, + }); + } catch { + return error("Kunne ikke sende mail!"); + } finally { + await client.close(); + } + return ok(undefined) +} + +export async function SendForgotPasswordMail(userEmail: string, name: string, verificationToken: string) { + try { + await client.connect({ + hostname: config.smtp_server, + port: config.smtp_port, + username: config.smtp_username, + password: config.smtp_password, + }); + + await client.send({ + from: "PasswordBox ", + to: userEmail, + subject: "PasswordBox - Glemt adgangskode", + content: `Hej ${name},

Der er blevet anmodet et password reset.
Klik på følgende link for at nulstille din adgangskode:

https://passwordbox.dk/verify?token=${verificationToken}

Hvis du ikke har glemt din adgangskode, kan du blot ignorere denne e-mail.

Med venlig hilsen,
PasswordBox

Denne e-mail kan ikke besvares`, + }); + } catch { + return error("Kunne ikke sende mail!"); + } finally { + await client.close(); + } + return ok(undefined) +} +