From 4fbc9819e8d5d160ab99256437bea9ff002726d1 Mon Sep 17 00:00:00 2001 From: Lightemerald Date: Sun, 24 Mar 2024 11:14:03 +0100 Subject: [PATCH] Role update - Added verify status to user login - Added option to disable email verification - Added roles route - Added role management to users --- .env.sample | 3 +- bun.lockb | Bin 97275 -> 97275 bytes index.js | 2 ++ routes/roles.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ routes/users.js | 48 +++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 routes/roles.js diff --git a/.env.sample b/.env.sample index 8d2906f..81e198c 100644 --- a/.env.sample +++ b/.env.sample @@ -7,4 +7,5 @@ DATABASE_PASSWORD="" JWT_SECRET="" SMTP= MAIL= -MAIL_PASS= \ No newline at end of file +MAIL_PASS= +DISABLE_EMAIL_VERIFICATION=true diff --git a/bun.lockb b/bun.lockb index 6bed908634736a42adf59a9a11cdcabf6d3214bd..0a0b0fde2b542566fe2108da16b40267400358cf 100755 GIT binary patch delta 1564 zcmezUo%Q#3)(uk=85t)}OAKdZocs<6~9+*4^Cgn0fB4uFm9GFzg1c}sv$!lOzFAF5n1}5); zNwaK_NFSJd1}5!tKqAw? z`oQEfFlko~5}5`j-+@WD3XsS=F!>Ek`c;BNmVwECU^1)lFabR*AnB=Pg ziKKzaePB|o79^4fCXazhxjK+Y8JIi=Ce`XeB6VQ$8kp2;0Ex7L$$MbZtPv#A2PU6^ zNxLSH$TTqd4otc=gGABj+Vz4& zrh&;U~(Ino8JPSBCc|cfMAm^xwmBen9GKh&Ci&)qMAE?IJ}@aZ4NW{W@~ePHq#n6z6A5}5`j-+@WD zB_NS`VDcN7^jiuNSq3KmfyuCCAdz)ol5IJN9S0`2fl0mT&m}J`oV#k5WZD5jbGe{&2Ozs1dVp~8Wd0_Gwn3UTJ z5-9_d=fI@eHjqdin7jri^|pgV+Q8&JFln{}B+>^apMgocogk5EJHsb05D=gIK!9a4 I$F9!@0WmoU-~a#s delta 1566 zcmezUo%Q#3)(uk=85t%|OAKd>oBR$;x+Q_w^T6acFzJ^J5?KZ&|AEP{6p+X|Fv*q* zV#k5WZD5iw4J48VCij6!v2>6~9+*4^Cgn0fB4uFm9GFzg1c}sv$!lOzFAF5n1}5); zNwaK_NFSJd1}5!tKqAw? z`oQEfFlko~5}5`j-+@WD3XsS=F!>Ek`c;BNmVwECU^1)lFabR*AnB=Pg ziKKzaePB|o79^4fCXazhxjK+Y8JIi=Ce`XeB6VQ$8kp2;0Ex7L$$MbZtPv#A2PU6^ zNxLSH$TTqd4otc=gGABj+Vz4& zrh&;U~(Ino8JPSBCc|cfMAm^xwmBen9GKh&Ci&)qMAE?IJ}@aZ4NW{W@~ePHq#n6z6A5}5`j-+@WD zB_NS`VDcN7^jiuNSq3KmfyuCCAdz)ol5IJN9S0`2fl0mT&m}J`oV#k5WZD5jbGe{&2Ozs1dVp~8Wd0_Gwn3UTJ z5-9_d=fI@eHjqdin7jri^|pgV+Q8&JFln{}B+>^apMgocogk5EU~-{=*yINSESouY HeLe^PbGc?d diff --git a/index.js b/index.js index 7aa88fe..f7faff3 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ import { speedLimiter, checkSystemLoad } from './modules/requestHandler'; import testRouter from './routes/test'; import usersRouter from './routes/users'; +import rolesRouter from './routes/roles'; import doctorsRouter from './routes/doctors'; import patientsRouter from './routes/patients'; import companiesRouter from './routes/companies'; @@ -37,6 +38,7 @@ app.use(express.static('public')); // routes app.use('/api/test', testRouter); app.use('/api/users', usersRouter); +app.use('/api/roles', rolesRouter); app.use('/api/doctors', doctorsRouter); app.use('/api/patients', patientsRouter); app.use('/api/companies', companiesRouter); diff --git a/routes/roles.js b/routes/roles.js new file mode 100644 index 0000000..2df91a4 --- /dev/null +++ b/routes/roles.js @@ -0,0 +1,92 @@ +import express from 'express'; +import { error } from '../modules/logManager'; +import { pool } from '../modules/databaseManager'; +import { verifyToken } from '../modules/tokenManager'; +import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler'; +import { checkBanned, checkPermissions } from '../modules/permissionManager'; + +const router = express.Router(); + + +// GET role list +router.get('/', verifyToken, checkBanned, checkPermissions('role', 1), async (req, res) => { + try { + const rows = await pool.execute('SELECT * FROM roles'); + if (rows[0].length === 0) return await respondWithStatus(res, 404, 'No roles found'); + return await respondWithStatusJSON(res, rows[0]); + + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +// POST create role +router.post('/', verifyToken, checkBanned, checkPermissions('role', 2), async (req, res) => { + const { name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield } = req.body; + if ([ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield ].every(Boolean)) { + try { + await pool.execute( + 'INSERT INTO users (name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield ], + ); + return await respondWithStatus(res, 200, 'Role created successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } + } + else { + return await respondWithStatus(res, 400, 'Missing fields'); + } +}); + +// GET role +router.get('/:id', verifyToken, checkBanned, checkPermissions('role', 1), async (req, res) => { + try { + const [rows] = await pool.execute('SELECT * FROM roles WHERE id = ? LIMIT 1', [ req.params.id ]); + if (rows.length === 0) return await respondWithStatus(res, 404, 'Role not found'); + return await respondWithStatusJSON(res, rows[0]); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +// PUT update role +router.put('/:id', verifyToken, checkBanned, checkPermissions('role', 2), async (req, res) => { + const { name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield } = req.body; + if ([ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield ].every(Boolean)) { + try { + await pool.execute( + 'UPDATE roles SET name = ?, user_bitfield = ?, role_bitfield = ?, verification_code_bitfield = ?, ban_bitfield = ?, patient_bitfield = ?, doctor_bitfield = ?, service_bitfield = ?, company_bitfield = ?, hospital_bitfield = ?, room_bitfield = ?, appointment_bitfield = ? WHERE id = ?', + [ name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield, req.params.id ], + ); + return await respondWithStatus(res, 200, 'Role updated successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } + } + else { + return await respondWithStatus(res, 400, 'Missing fields'); + } +}); + +// DELETE role +router.delete('/:id', verifyToken, checkBanned, checkPermissions('role', 4), async (req, res) => { + try { + await pool.execute('DELETE FROM roles WHERE id = ?', [ req.params.id ]); + return await respondWithStatus(res, 200, 'Role deleted successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +export default router; \ No newline at end of file diff --git a/routes/users.js b/routes/users.js index b7f863e..c0a2dc1 100644 --- a/routes/users.js +++ b/routes/users.js @@ -30,6 +30,8 @@ router.post('/register', requestLimiter, async (req, res) => { ); if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing user'); + if (process.env.DISABLE_EMAIL_VERIFICATION) return await respondWithStatus(res, 200, 'Successfully registered'); + const [rows] = await pool.execute('SELECT id FROM users WHERE email = ? LIMIT 1', [email]); const code = sendVerification(email, rows[0].id, 'email'); pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ rows[0].id, code, 'email' ]); @@ -74,6 +76,7 @@ router.post('/login', requestLimiter, async (req, res) => { email: user.email, first_name: user.first_name, last_name: user.last_name, + verified_status: process.env.DISABLE_EMAIL_VERIFICATION ? true : user.email_verified, }, }); } @@ -322,4 +325,49 @@ router.delete('/:userId', verifyToken, checkBanned, async (req, res) => { } }); +router.get('/:userId/roles', verifyToken, checkBanned, async (req, res) => { + try { + if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 1)) return await respondWithStatus(res, 403, 'Missing permission'); + const [rows] = await pool.execute('SELECT r.* FROM users u INNER JOIN user_roles ur ON u.id = ur.user_id INNER JOIN roles r ON ur.role_id = r.id WHERE u.id = ?', [ req.params.userId ]); + if (rows.length === 0) return await respondWithStatus(res, 404, 'No roles found'); + return await respondWithStatusJSON(res, rows); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + +router.post('/:userId/roles', verifyToken, checkBanned, checkPermissions('user', 2), async (req, res) => { + const { roleId } = req.body; + if (roleId) { + try { + const [rows] = await pool.execute('SELECT * FROM roles WHERE id = ? LIMIT 1', [ roleId ]); + if (rows.length === 0) return await respondWithStatus(res, 404, 'Role not found'); + const [result] = await pool.execute('INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)', [ req.params.userId, roleId ]); + if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error assigning role'); + return await respondWithStatus(res, 200, 'Role assigned successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } + } + else { + return await respondWithStatus(res, 400, 'Missing fields'); + } +}); + +router.delete('/:userId/roles/:roleId', verifyToken, checkBanned, checkPermissions('user', 4), async (req, res) => { + try { + const [result] = await pool.execute('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?', [ req.params.userId, req.params.roleId ]); + if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error removing role'); + return await respondWithStatus(res, 200, 'Role removed successfully'); + } + catch (err) { + error(err); + return await respondWithStatus(res, 500, 'An error has occured'); + } +}); + export default router; \ No newline at end of file