From 038b809dd8eb73adc18cfb36cf73631f1139b65b Mon Sep 17 00:00:00 2001 From: andrei-mihnea-cerbu Date: Wed, 3 Apr 2024 09:50:48 +0300 Subject: [PATCH] API CEO + USER v1.0 --- UC/backend/src/db/ceo.json | 1 + UC/backend/src/db/departments.json | 11 +- UC/backend/src/models/schemaMapper.js | 4 +- UC/backend/src/models/securityLevelsModel.js | 19 +++ UC/backend/src/routes/admin.js | 8 +- UC/backend/src/routes/ceo.js | 167 +++++++++++++++++-- UC/backend/src/routes/users.js | 18 +- 7 files changed, 212 insertions(+), 16 deletions(-) create mode 100644 UC/backend/src/models/securityLevelsModel.js diff --git a/UC/backend/src/db/ceo.json b/UC/backend/src/db/ceo.json index 2b10c9a..f18b767 100644 --- a/UC/backend/src/db/ceo.json +++ b/UC/backend/src/db/ceo.json @@ -1,4 +1,5 @@ { + "id": "335ae110091d3bc5fbc90d5e3786890e", "name": "CEO", "email": "ceo@yourfirm.com", "password": "335ae110091d3bc5fbc90d5e3786890e" diff --git a/UC/backend/src/db/departments.json b/UC/backend/src/db/departments.json index 0637a08..69291c4 100644 --- a/UC/backend/src/db/departments.json +++ b/UC/backend/src/db/departments.json @@ -1 +1,10 @@ -[] \ No newline at end of file +{ + "1": { + "name": "HR", + "key": "65388e02b16e1cc530502492384ebd29ee6cda57840235d714d59dd460ae148a" + }, + "2": { + "name": "Contabili", + "key": "572854d54c42f03ddcbb2beeae574dc4fcd33b251670d4983be49c4242c2d88a" + } +} \ No newline at end of file diff --git a/UC/backend/src/models/schemaMapper.js b/UC/backend/src/models/schemaMapper.js index fb93d43..54eb32d 100644 --- a/UC/backend/src/models/schemaMapper.js +++ b/UC/backend/src/models/schemaMapper.js @@ -7,6 +7,7 @@ const ceoModifyModelSchema = require('./ceoModifyModel'); const emailVerificationSchema = require('./emailVerificationModel'); const userDepartmentPatchSchema = require('./userDepartmentPatchModel'); const ceoPasswordModelSchema = require('./ceoPasswordModel'); +const securityLevelsModelSchema = require('./securityLevelsModel'); module.exports = { schemas: { @@ -18,6 +19,7 @@ module.exports = { ceoModifyModelSchema, emailVerificationSchema, userDepartmentPatchSchema, - ceoPasswordModelSchema + ceoPasswordModelSchema, + securityLevelsModelSchema }, }; \ No newline at end of file diff --git a/UC/backend/src/models/securityLevelsModel.js b/UC/backend/src/models/securityLevelsModel.js new file mode 100644 index 0000000..83feda6 --- /dev/null +++ b/UC/backend/src/models/securityLevelsModel.js @@ -0,0 +1,19 @@ +const Joi = require("joi"); + +// Define a schema for the string values (department names) +const nameSchema = Joi.string().required().messages({ + 'string.empty': 'Name can\'t be empty', + 'any.required': 'Name is required' +}); + +// Define the main schema for the security levels model +const securityLevelsModel = Joi.object().pattern( + Joi.number().integer().positive().messages({ + 'number.base': 'Keys must be integers', + 'number.integer': 'Keys must be integers', + 'number.positive': 'Keys must be positive integers' + }), + nameSchema +); + +module.exports = securityLevelsModel; diff --git a/UC/backend/src/routes/admin.js b/UC/backend/src/routes/admin.js index 7c8055a..ff93068 100644 --- a/UC/backend/src/routes/admin.js +++ b/UC/backend/src/routes/admin.js @@ -1,7 +1,10 @@ const express = require('express'); -const {httpStatus} = require("../helpers/httpResponses"); -const router = express.Router(); const crypto = require('crypto'); +const { v4: uuidv4 } = require('uuid'); + +const {httpStatus} = require("../helpers/httpResponses"); + +const router = express.Router(); function validateBody(req, res, next) { const apiKey = req.headers['authorization']; @@ -26,6 +29,7 @@ router.get('/reset_server', validateBody, (req, res) => { departmentsDB.writeFile([]); backupSchemesDB.writeFile({}); ceoDB.writeFile({ + id: uuidv4(), name: "CEO", email: "ceo@yourfirm.com", password: crypto.randomBytes(16).toString('hex') diff --git a/UC/backend/src/routes/ceo.js b/UC/backend/src/routes/ceo.js index 81e15e4..84ba1cd 100644 --- a/UC/backend/src/routes/ceo.js +++ b/UC/backend/src/routes/ceo.js @@ -1,40 +1,187 @@ const express = require('express'); +const { v4: uuidv4 } = require('uuid'); + +const {schemas} = require("../models/schemaMapper"); +const {httpStatus} = require("../helpers/httpResponses"); +const crypto = require("crypto"); +const {departmentsDB} = require("../db/jsonDatabaseManager"); + const router = express.Router(); function validateBody(req, res, next) { + if(req.path !== '/login'){ + const ceoDB = req.app.get('ceoDB'); + const ceoPassword = req.headers['ceo_password']; + const {password} = ceoDB.readFile() + + if(ceoPassword !== password){ + return res.status(httpStatus.UNAUTHORIZED).json({message: 'Invalid CEO password.'}); + } + } + + let validationSchema = undefined; + + switch(req.path){ + case '/set_security_levels': + validationSchema = schemas.securityLevelsModelSchema; + break; + case '/login': + validationSchema = schemas.usersLoginModelSchema; + break; + case '/departments': + validationSchema = schemas.departmentRegisterModelSchema; + break; + case '/': + if(req.method === 'PUT'){ + validationSchema = schemas.ceoModifyModelSchema; + } + break; + default: + validationSchema = undefined; + } + + if(validationSchema !== undefined){ + const {error} = validationSchema.validate(req.body); + if(error){ + const errorMessage = error.details.map(detail => detail.message).join(', '); + return res.status(httpStatus.BAD_REQUEST).json({ message: errorMessage }); + } + } + next(); } router.post('/login', validateBody, (req, res) => { + const ceoDB = req.app.get('ceoDB'); const { email, password } = req.body; + + if(ceoDB.findIndexByKeyValueInArray('email', email) !== ceoDB.findIndexByKeyValueInArray('password', password)){ + return res.status(httpStatus.UNAUTHORIZED).json({message: 'Invalid credentials.'}); + } + + return res.status(httpStatus.Ok).json({message: 'Logged in.'}); }); -router.get('/set_security_levels', validateBody, (req, res) => { +router.post('/set_security_levels', validateBody, (req, res) => { + const departmentsDB = req.app.get('departmentsDB'); + const departmentsJson = departmentsDB.readFile(); + + const levelsJson = req.body; + console.log(levelsJson); + + const newJson = {}; + + Object.entries(levelsJson).forEach(([levelKey, departmentName]) => { + let departmentId = -1; + for (let deptKey in departmentsJson) { + if (departmentsJson.hasOwnProperty(deptKey)) { + if (departmentsJson[deptKey].name === departmentName) { + departmentId = deptKey; + break; + } + } + } + + if (departmentId !== -1) { + newJson[levelKey] = { + name: departmentName, + key: departmentsJson[departmentId].key + }; + } + }); + + + departmentsDB.writeFile(newJson); + return res.status(httpStatus.OK).json({message: 'Departments levels updated.'}); }); -router.get('/users/delete/:id', validateBody, (req, res) => { +router.delete('/users/:id', validateBody, (req, res) => { + const usersDB = req.app.get('usersDB'); const { id } = req.params; + + const userIndex = usersDB.findIndexByKeyValueInArray('id', id); + if(userIndex === -1){ + return res.status(httpStatus.NOT_FOUND).json({message: 'User not found in system.'}); + } + + let usersJson = usersDB.readFile; + usersJson.splice(userIndex, 1); + usersDB.writeFile(usersJson); + + return res.status(httpStatus.OK).json({ message: 'User deleted successfully.' }); }); router.post('/departments', validateBody, (req, res) => { + const departmentsDB = req.app.get('departmentsDB'); const { name } = req.body; - // Generate key with crypto of 32Hex + + let jsonDepartments = departmentsDB.readFile(); + const nameExists = Object.values(jsonDepartments).some(department => department.name === name); + if(nameExists){ + return res.status(httpStatus.CONFLICT).json({message: 'Department already in system.'}) + } + + const isObjectEmpty = !Object.keys(jsonDepartments).length; + const highestKey = isObjectEmpty ? 0 : Math.max(...Object.keys(jsonDepartments).map(Number)); + const nextKey = highestKey + 1; + + jsonDepartments[nextKey] = { + name: name, + key: crypto.randomBytes(32).toString('hex') + }; + departmentsDB.writeFile(jsonDepartments); + + return res.status(httpStatus.CREATED).json({message: 'Department successfully created'}); }); -router.get('/departments/:id', validateBody, (req, res) => { - const { id } = req.params; +router.delete('/departments/:name', validateBody, (req, res) => { + const departmentsDB = req.app.get('departmentsDB'); + let departmentsJson = departmentsDB.readFile(); + let foundName = false; + const { name } = req.params; + + console.log(name); + + for (let key in departmentsJson) { + if (departmentsJson.hasOwnProperty(key)) { + const department = departmentsJson[key]; + if (department.name === name) { + delete departmentsJson[key]; + foundName = true; + break; + } + } + } + + if(foundName !== true){ + return res.status(httpStatus.NOT_FOUND).json({ message: 'Department not found.' }); + } + + departmentsDB.writeFile(departmentsJson); + return res.status(httpStatus.OK).json({ message: 'Department deleted successfully.' }); }); router.put('/', validateBody, (req, res) => { - const { email } = req.body; -}); + const ceoDB = req.app.get('ceoDB'); + const { name, email } = req.body; + const {id} = ceoDB.readFile() -router.get('/', validateBody, (req, res) => { - // Send CEO info + const newCeo = { + id: id, + name: name, + email: email, + password: crypto.randomBytes(16).toString('hex') + } + + ceoDB.writeFile(newCeo); + return res.status(httpStatus.OK).json({message: 'Information modified.'}); }); router.get('/get_decrypt_keys', (req, res) => { - // Get all departments + return res.status(httpStatus.OK).json({ + message: 'Departments fetched.', + data: departmentsDB.readFile() + }); }); module.exports = router; diff --git a/UC/backend/src/routes/users.js b/UC/backend/src/routes/users.js index b7f4b5c..e1651c0 100644 --- a/UC/backend/src/routes/users.js +++ b/UC/backend/src/routes/users.js @@ -90,12 +90,26 @@ router.post('/validate_email', validateBody, (req, res) => { router.get('/', (req, res) => { const usersDB = req.app.get('usersDB'); + const ceoDB = req.app.get('ceoDB'); + + const ceoJson = ceoDB.readFile(); + let usersJson = usersDB.readFile(); + usersJson.push(ceoJson); + return res.status(httpStatus.OK).json({ message: "Fetched users", - data: usersDB.readFile() + data: usersJson }) }); +router.get('/departments', (req, res) => { + const departmentsDB = req.app.get('departmentsDB'); + return res.status(httpStatus.OK).json({ + message: 'Departments fetched.', + data: departmentsDB.readFile() + }); +}) + router.put('/', validateBody, (req, res) => { const usersDB = req.app.get('usersDB'); let usersJson = usersDB.readFile(); @@ -161,7 +175,7 @@ router.get('/get_decrypt_keys', (req, res) => { return res.status(httpStatus.OK).json({ message: 'Departments fetched.', data: departmentsDB.readFile() - }) + }); }); router.use((req, res) => {