diff --git a/UC/backend/README.md b/UC/backend/README.md new file mode 100644 index 0000000..67665ec --- /dev/null +++ b/UC/backend/README.md @@ -0,0 +1,45 @@ +General Overview +- + +The UC serves as the main logistic station for all the client/CEO applications which are used +inside the corporation. This UC stores +- credentials for the users and ceo; +- registered departments, as well security levels and the encrypting/decrypting keys for the files; +- backup schemes for each user, as well as the IP, as well as the IPs where each computer is located; + +The main endpoints of the application are: + +- Users: + + - Can register/login in the system; + - Update the information (username, email, password); + - Change the working department (the client app will prompt the CEO password request to proceed); + - Retrieve the full list of departments for the sign-up / 'change department' procedure; + - Retrieve the full list of users and their IPs for sharing a file + + +- CEO: + + - Can log in the system; + - Can change the email (the password can only be retrieved by the admin); + - Delete users from the system; + - Create / Delete departments, as well as managing the security levels of each department; + - Retrieve the full list of users and their IPs for sharing a file + + +- Admin: + + - Reset the server to default state; + - Retrieve the CEO login information, as well as a list of all users in the system; + + +- Backup Schemes: + + - At a request, will get the ID of a user, their IP and the backup directory structure to place it in the scheme, + as well as the data size of the backup + + +Other Features +- +The UC will also create a separate process which at a set interval of time will retrieve from the admin config, as +well as from the CEO config, the ID so that it will be used to authenticate the users at their endpoints. diff --git a/UC/backend/src/app.js b/UC/backend/src/app.js index 0499cc9..8d53b3c 100644 --- a/UC/backend/src/app.js +++ b/UC/backend/src/app.js @@ -8,23 +8,21 @@ const port = process.env.PORT || 5000; app.use(cors()); app.use(json()); -app.locals.apiKey = 'uc_api' - const checkJson = require('./middlewares/checkJson'); const apiKeyValidation = require('./middlewares/apiKeyValidation'); -const setAdminValues = require('./middlewares/setAdminValues'); app.use(apiKeyValidation) app.use(checkJson) -app.use(setAdminValues); const usersRouter = require('./routes/users'); -const departmentsRouter = require('./routes/departments'); const adminRouter = require('./routes/admin'); +const ceoRouter = require('./routes/ceo'); +const backupSchemesRouter = require('./routes/backup_schemes'); app.use('/users', usersRouter); -app.use('/departments', departmentsRouter); app.use('/admin', adminRouter); +app.use('/ceo', ceoRouter); +app.use('/backup_schemes', backupSchemesRouter); app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); @@ -32,8 +30,7 @@ app.listen(port, () => { // Graceful shutdown function function gracefulShutdown() { - console.log('\nGracefully shutting down from SIGINT (Ctrl-C)'); - + console.log('\nShutting down!'); process.exit(0); } diff --git a/UC/backend/src/db/backup_schemas.json b/UC/backend/src/db/backup_schemas.json deleted file mode 100644 index 9e26dfe..0000000 --- a/UC/backend/src/db/backup_schemas.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/UC/backend/src/db/config.json b/UC/backend/src/db/config.json deleted file mode 100644 index c878440..0000000 --- a/UC/backend/src/db/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "adminKey": "9ded8e96a7a03fb7ce23a19b4f6a3d08ac42926aa96664af941e4d02e0b93f0d", - "ceoID": "a7635c7a-d6a0-43dc-8e24-552ab33239b8" -} \ No newline at end of file diff --git a/UC/backend/src/db/departments.json b/UC/backend/src/db/departments.json deleted file mode 100644 index 1a39bce..0000000 --- a/UC/backend/src/db/departments.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "id": "16183532-3090-4e24-8def-aa29f316c1c5", - "name": "Programatori", - "key": "66238d3abd92e43486a1f8526ff83646a2481b4fb669667daf81ecc71e8211e3" - }, - { - "id": "897b883b-7638-452e-bed9-81e8da2ec0c3", - "name": "Contabili", - "key": "55956d2a1cad9e3952f3756ded721724f71baada95c9addbf46f72defa06d354" - } -] \ No newline at end of file diff --git a/UC/backend/src/db/security_levels.json b/UC/backend/src/db/security_levels.json deleted file mode 100644 index 1889482..0000000 --- a/UC/backend/src/db/security_levels.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "1": "f1444987-c0c2-4ce8-89e3-b21f302bff7f", - "2": "16183532-3090-4e24-8def-aa29f316c1c5", - "3": "897b883b-7638-452e-bed9-81e8da2ec0c3" -} \ No newline at end of file diff --git a/UC/backend/src/db/users.json b/UC/backend/src/db/users.json deleted file mode 100644 index ba2a260..0000000 --- a/UC/backend/src/db/users.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "id": "a7635c7a-d6a0-43dc-8e24-552ab33239b8", - "name": "CEO", - "email": "ceo@yourfirm.com", - "password": "405ee6885be5385223830874bd6dcfca", - "department": "" - }, - { - "id": "ffc90aca-bb31-4641-9a43-30b343924e8a", - "name": "Andrei", - "email": "a@c.com", - "password": "5447045dc3cfaafdb49b365e31dad41eb88616b0b1449930e55fd3a70bd8cee4", - "department": "16183532-3090-4e24-8def-aa29f316c1c5" - } -] \ No newline at end of file diff --git a/UC/backend/src/exceptions/departmentAlreadyExistsError.js b/UC/backend/src/exceptions/departmentAlreadyExistsError.js deleted file mode 100644 index 64ab7b3..0000000 --- a/UC/backend/src/exceptions/departmentAlreadyExistsError.js +++ /dev/null @@ -1,8 +0,0 @@ -class DepartmentAlreadyExistsError extends Error { - constructor(message = 'This department is already in system') { - super(message); - this.name = 'DepartmentAlreadyExistsError'; - } -} - -module.exports = DepartmentAlreadyExistsError \ No newline at end of file diff --git a/UC/backend/src/exceptions/departmentNotExistingError.js b/UC/backend/src/exceptions/departmentNotExistingError.js deleted file mode 100644 index 49f57e6..0000000 --- a/UC/backend/src/exceptions/departmentNotExistingError.js +++ /dev/null @@ -1,8 +0,0 @@ -class DepartmentNotExistingError extends Error { - constructor(message = 'Department not found') { - super(message); - this.name = 'DepartmentNotExistingError'; - } -} - -module.exports = DepartmentNotExistingError; \ No newline at end of file diff --git a/UC/backend/src/exceptions/emailAlreadyInSystemError.js b/UC/backend/src/exceptions/emailAlreadyInSystemError.js deleted file mode 100644 index b3f5af9..0000000 --- a/UC/backend/src/exceptions/emailAlreadyInSystemError.js +++ /dev/null @@ -1,8 +0,0 @@ -class EmailAlreadyInSystemError extends Error { - constructor(message = "Email is already in system!") { - super(message); - this.name = 'EmailAlreadyInSystem'; - } -} - -module.exports = EmailAlreadyInSystemError \ No newline at end of file diff --git a/UC/backend/src/exceptions/improperDirStructureError.js b/UC/backend/src/exceptions/improperDirStructureError.js deleted file mode 100644 index 004490e..0000000 --- a/UC/backend/src/exceptions/improperDirStructureError.js +++ /dev/null @@ -1,8 +0,0 @@ -class ImproperDirStructureError extends Error { - constructor(message = 'the provided structure is not a valid JSON') { - super(message); - this.name = 'ImproperDirStructureError'; - } -} - -module.exports = ImproperDirStructureError \ No newline at end of file diff --git a/UC/backend/src/exceptions/invalidDepartmentJsonError.js b/UC/backend/src/exceptions/invalidDepartmentJsonError.js deleted file mode 100644 index 5d616af..0000000 --- a/UC/backend/src/exceptions/invalidDepartmentJsonError.js +++ /dev/null @@ -1,8 +0,0 @@ -class InvalidDepartmentJsonError extends Error { - constructor(message = 'Invalid JSON format for security levels') { - super(message); - this.name = 'InvalidDepartmentJsonError'; - } -} - -module.exports=InvalidDepartmentJsonError; \ No newline at end of file diff --git a/UC/backend/src/exceptions/invalidKeyErrorException.js b/UC/backend/src/exceptions/invalidKeyErrorException.js deleted file mode 100644 index 805137d..0000000 --- a/UC/backend/src/exceptions/invalidKeyErrorException.js +++ /dev/null @@ -1,8 +0,0 @@ -class InvalidKeyErrorException extends Error { - constructor(message = 'Invalid key for this operation') { - super(message); - this.name = 'InvalidKeyErrorException'; - } -} - -module.exports=InvalidKeyErrorException; \ No newline at end of file diff --git a/UC/backend/src/exceptions/userAlreadyExistsError.js b/UC/backend/src/exceptions/userAlreadyExistsError.js deleted file mode 100644 index 4ee1a4e..0000000 --- a/UC/backend/src/exceptions/userAlreadyExistsError.js +++ /dev/null @@ -1,8 +0,0 @@ -class UserAlreadyExistsException extends Error { - constructor(message = 'User already exists in system') { - super(message); - this.name = 'UserAlreadyExistsException'; - } -} - -module.exports = UserAlreadyExistsException \ No newline at end of file diff --git a/UC/backend/src/exceptions/userNotExistingError.js b/UC/backend/src/exceptions/userNotExistingError.js deleted file mode 100644 index 0b9f088..0000000 --- a/UC/backend/src/exceptions/userNotExistingError.js +++ /dev/null @@ -1,8 +0,0 @@ -class UserNotExistingError extends Error { - constructor(message = 'User not found in the system!') { - super(message); - this.name = 'UserNotExistingError'; - } -} - -module.exports = UserNotExistingError \ No newline at end of file diff --git a/UC/backend/src/middlewares/setAdminValues.js b/UC/backend/src/middlewares/setAdminValues.js deleted file mode 100644 index b9b83d6..0000000 --- a/UC/backend/src/middlewares/setAdminValues.js +++ /dev/null @@ -1,26 +0,0 @@ -const path = require("path"); -const fs = require("fs"); - -function SetAdminValuesMiddleware(req, res, next){ - try{ - const configFilePath = path.join(__dirname, '..', 'db', 'config.json'); - const config = JSON.parse(fs.readFileSync(configFilePath)); - - res.locals.adminKey = config.adminKey === undefined ? " " : config.adminKey; - res.locals.ceoID = config.ceoID === undefined ? " " : config.ceoID; - - next(); - - fs.writeFileSync(configFilePath, JSON.stringify( - { - adminKey: res.locals.adminKey, - ceoID: res.locals.ceoID - }, null, 2)); - return res; - } catch(error){ - console.log(error.message); - return res; - } -} - -module.exports = SetAdminValuesMiddleware; \ No newline at end of file diff --git a/UC/backend/src/routes/admin.js b/UC/backend/src/routes/admin.js index 4467d5a..f38bd10 100644 --- a/UC/backend/src/routes/admin.js +++ b/UC/backend/src/routes/admin.js @@ -1,153 +1,20 @@ const express = require('express'); -const fs = require('fs'); -const crypto = require('crypto'); -const {v4: uuidv4} = require("uuid"); -const path = require("path"); - -const { sendResponse } = require('../helpers/responseHelper'); -const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses') -const {schemas} = require("../models/schemaMapper"); -const InvalidKeyErrorException = require("../exceptions/invalidKeyErrorException"); -const UserNotExistingError = require("../exceptions/userNotExistingError"); - const router = express.Router(); -router.get('/reset_key', (req, res) => { - - const adminKey= crypto.randomBytes(32).toString('hex'); - res.locals.adminKey = adminKey; - sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], adminKey); -}); - -router.get('/reset', (req, res) => { - try { - const requestAdminKey = req.headers['admin-key']; - - if (requestAdminKey !== res.locals.adminKey) { - throw new InvalidKeyErrorException(); - } - - fs.writeFileSync(path.join(__dirname, '..', 'db', 'users.json'), '[]'); - fs.writeFileSync(path.join(__dirname, '..', 'db', 'departments.json'), '[]'); - fs.writeFileSync(path.join(__dirname, '..', 'db', 'security_levels.json'), '{}'); - fs.writeFileSync(path.join(__dirname, '..', 'db', 'backup_schemas.json'), '{}'); - fs.writeFileSync(path.join(__dirname, '..', 'db', 'config.json'), JSON.stringify({ - adminKey: ' ', - ceoID: ' ' - }, null, 2)); - - const adminPassword = crypto.randomBytes(16).toString('hex'); // Generating random password - const ceoID = uuidv4(); - res.locals.ceoID = ceoID; - const adminAccount = { - id: ceoID, - name: 'CEO', - email: 'ceo@yourfirm.com', - password: adminPassword - }; - - const usersData = [adminAccount]; - fs.writeFileSync(path.join(__dirname, '..', 'db', 'users.json'), - JSON.stringify(usersData, null, 2)); - - sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]); - } catch (error) { - console.log(error.message); - if(error instanceof InvalidKeyErrorException){ - return sendResponse(res, httpStatus.UNAUTHORIZED, error.message); - } - - sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -}); - -const validateRegisterCeoBody = (req, res, next) => { - const { error, value } = schemas.ceoRegisterModelSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; +function validateBody(req, res, next) { next(); } -router.put('/change_admin_credentials', validateRegisterCeoBody, (req, res) => { - try { - const requestAdminKey = req.headers['admin-key']; - if (requestAdminKey !== res.locals.adminKey) { - throw new InvalidKeyErrorException(); - } - - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = JSON.parse(fs.readFileSync(usersFilePath, 'utf8')); - - const adminIndex = usersData.findIndex(user => user.id === res.locals.ceoID); - if (adminIndex === -1) { - throw new UserNotExistingError(); - } - - const { name, email } = req.body; // Using req.body instead of req.jsonModel - const newPassword = crypto.randomBytes(16).toString('hex'); // Generate new random password - - // Update the user data - usersData[adminIndex] = { - id: res.locals.ceoID, - name: name, - email: email, - password: newPassword - }; - - // Write the updated user data back to the file - fs.writeFileSync(usersFilePath, JSON.stringify(usersData, null, 2)); - - sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.log(error.message); - if (error instanceof InvalidKeyErrorException) { - return sendResponse(res, httpStatus.UNAUTHORIZED, error.message); - } - if (error instanceof UserNotExistingError) { - return sendResponse(res, httpStatus.NOT_FOUND, error.message); - } - - sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]) - } +router.post('/reset_server', validateBody, (req, res) => { + // Handle reset server logic here }); -router.post('/validate_admin_password', (req, res) => { - try { - const requestAdminKey = req.headers['admin-key']; - if (requestAdminKey !== res.locals.adminKey) { - console.log(res.locals.adminKey); - throw new InvalidKeyErrorException(); - } - - if(req.body.password === undefined){ - return sendResponse(res, httpStatus.BAD_REQUEST, httpStatusMessages[httpStatus.BAD_REQUEST]); - } - const password = req.body.password; - - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - const user = usersJson.find(user => user.id === res.locals.ceoID); - if(user.password !== password){ - return sendResponse(res, httpStatus.UNAUTHORIZED, httpStatusMessages[httpStatus.UNAUTHORIZED]); - } - - sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.log(error.message); - if(error instanceof InvalidKeyErrorException){ - return sendResponse(res, httpStatus.UNAUTHORIZED, error.message); - } - - sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.get('/ceo', validateBody, (req, res) => { + // Retrieve CEO info }); -router.use((req, res) => { - return sendResponse(res, httpStatus.NOT_FOUND, "Path not found"); +router.get('/users', validateBody, (req, res) => { + // Retrieve all users in the system }); module.exports = router; diff --git a/UC/backend/src/routes/backup_schemes.js b/UC/backend/src/routes/backup_schemes.js new file mode 100644 index 0000000..aa2d366 --- /dev/null +++ b/UC/backend/src/routes/backup_schemes.js @@ -0,0 +1,15 @@ +const express = require('express'); +const router = express.Router(); + +function validateBody(req, res, next) { + next(); +} + +router.patch('/', validateBody, (req, res) => { + const { id, ip, backup_schema, size } = req.body; +}); + +router.get('/', validateBody, (req, res) => { +}); + +module.exports = router; diff --git a/UC/backend/src/routes/ceo.js b/UC/backend/src/routes/ceo.js new file mode 100644 index 0000000..81e15e4 --- /dev/null +++ b/UC/backend/src/routes/ceo.js @@ -0,0 +1,40 @@ +const express = require('express'); +const router = express.Router(); + +function validateBody(req, res, next) { + next(); +} + +router.post('/login', validateBody, (req, res) => { + const { email, password } = req.body; +}); + +router.get('/set_security_levels', validateBody, (req, res) => { +}); + +router.get('/users/delete/:id', validateBody, (req, res) => { + const { id } = req.params; +}); + +router.post('/departments', validateBody, (req, res) => { + const { name } = req.body; + // Generate key with crypto of 32Hex +}); + +router.get('/departments/:id', validateBody, (req, res) => { + const { id } = req.params; +}); + +router.put('/', validateBody, (req, res) => { + const { email } = req.body; +}); + +router.get('/', validateBody, (req, res) => { + // Send CEO info +}); + +router.get('/get_decrypt_keys', (req, res) => { + // Get all departments +}); + +module.exports = router; diff --git a/UC/backend/src/routes/departments.js b/UC/backend/src/routes/departments.js deleted file mode 100644 index eeb880c..0000000 --- a/UC/backend/src/routes/departments.js +++ /dev/null @@ -1,224 +0,0 @@ -const express = require('express'); -const fs = require('fs'); -const path = require('path'); -const { v4: uuidv4 } = require('uuid'); -const crypto = require('crypto'); - -const { sendResponse } = require('../helpers/responseHelper'); -const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses') -const {schemas} = require("../models/schemaMapper"); - -const DepartmentAlreadyExistsError = require("../exceptions/departmentAlreadyExistsError"); -const InvalidDepartmentJsonError = require("../exceptions/invalidDepartmentJsonError"); -const DepartmentNotExistingError = require("../exceptions/departmentNotExistingError"); - -const router = express.Router(); - -router.get('/', (req, res) => { - try { - const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json'); - const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8'); - const departments = JSON.parse(departmentsData); - - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], departments); - } catch (error) { - console.error(`Error: ${error.message}`); - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -}); - - -function generateAESKey() { - return crypto.randomBytes(32).toString('hex'); -} - -router.post('/', validateRegisterBody, createDepartment, addSecurityLevel, (req, res) => { - return res; -}); - -function validateRegisterBody(req, res, next) { - const { error, value } = schemas.departmentRegisterModelSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; - next(); -} - -function createDepartment(req, res, next) { - try { - const { name } = req.jsonModel; - - const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json'); - const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8'); - const departments = JSON.parse(departmentsData); - - const existingDepartment = departments.find(department => department.name === name); - if (existingDepartment) { - throw new DepartmentAlreadyExistsError(); - } - - const departmentId = uuidv4(); - const aesKey = generateAESKey(); - const newDepartment = { - id: departmentId, - name: name, - key: aesKey - }; - - departments.push(newDepartment); - fs.writeFileSync(departmentsFilePath, JSON.stringify(departments, null, 2)); - - req.departmentId = departmentId; - next(); - - return res; - } catch (error) { - console.error(`Error: ${error.message}`); - if (error instanceof DepartmentAlreadyExistsError) { - return sendResponse(res, httpStatus.CONFLICT, error.message); - } - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -} - -function addSecurityLevel(req, res) { - try { - const { departmentId } = req; - - const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json'); - const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8'); - const securityLevels = JSON.parse(securityLevelsData); - - const nextIndex = Object.keys(securityLevels).length + 1; - securityLevels[nextIndex] = departmentId; - - fs.writeFileSync(securityLevelsFilePath, JSON.stringify(securityLevels, null, 2)); - - return sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]); - } catch (error) { - console.error(`Error: ${error.message}`); - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -} - - -router.put('/', validateDepartmentsJson, validateSecurityLevels, updateSecurityLevels); - -function validateDepartmentsJson(req, res, next) { - try { - for (const key in req.body) { - if (!Number.isInteger(parseInt(key)) || typeof req.body[key] !== 'string') { - throw new InvalidDepartmentJsonError(); - } - } - - next(); - } catch (error) { - console.error(`Error: ${error.message}`); - return sendResponse(res, httpStatus.BAD_REQUEST, error.message); - } -} - -function validateSecurityLevels(req, res, next) { - try { - const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json'); - const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8'); - const securityLevels = JSON.parse(securityLevelsData); - - if (Object.keys(req.body).length !== Object.keys(securityLevels).length) { - throw new InvalidDepartmentJsonError('Number of keys does not match the database'); - } - - const encounteredValues = new Set(); - const securityLevelValues = Object.values(securityLevels); - - for (const key in req.body) { - const requestBodyValue = req.body[key]; - if (!securityLevels.hasOwnProperty(key)) { - throw new InvalidDepartmentJsonError('Invalid key found in request body'); - } - const securityLevelValue = securityLevels[key]; - - if (!securityLevelValues.includes(securityLevelValue)) { - throw new InvalidDepartmentJsonError('Values do not match the database'); - } - - if (encounteredValues.has(requestBodyValue)) { - throw new InvalidDepartmentJsonError('Duplicate values found in request body'); - } - - encounteredValues.add(requestBodyValue); - } - - next(); - return res; - } catch (error) { - console.error(`Error: ${error.message}`); - return sendResponse(res, httpStatus.BAD_REQUEST, error.message); - } -} - -function updateSecurityLevels(req, res) { - try { - const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json'); - fs.writeFileSync(securityLevelsFilePath, JSON.stringify(req.body, null, 2)); - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.error(`Error: ${error.message}`); - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -} - - -router.delete('/:id', async (req, res) => { - const id = req.params.id; - - try { - deleteDepartment(id, res); - } catch (error) { - console.error(`Error: ${error.message}`); - - if (error instanceof DepartmentNotExistingError) { - return sendResponse(res, httpStatus.NOT_FOUND, error.message); - } - - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -}); - -function deleteDepartment(id, res) { - const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json'); - const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8'); - const departmentsJson = JSON.parse(departmentsData); - - const index = departmentsJson.findIndex(department => department.id === id); - if (index === -1) { - throw new DepartmentNotExistingError(); - } - - departmentsJson.splice(index, 1); - fs.writeFileSync(departmentsFilePath, JSON.stringify(departmentsJson, null, 2)); - - const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json'); - const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8'); - const securityLevelsJson = JSON.parse(securityLevelsData); - - const securityIndex = securityLevelsJson.findIndex(level => level.id === id); - if (securityIndex !== -1) { - securityLevelsJson.splice(securityIndex, 1); - fs.writeFileSync(securityLevelsFilePath, JSON.stringify(securityLevelsJson, null, 2)); - } - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); -} - - - -router.use((req, res) => { - return sendResponse(res, httpStatus.NOT_FOUND, "Path not found"); -}); - -module.exports = router; diff --git a/UC/backend/src/routes/users.js b/UC/backend/src/routes/users.js index 0eb69af..b871e98 100644 --- a/UC/backend/src/routes/users.js +++ b/UC/backend/src/routes/users.js @@ -1,359 +1,34 @@ const express = require('express'); -const fs = require('fs'); -const path = require('path'); -const {v4: uuidv4, validate} = require('uuid'); -const crypto = require('crypto'); - -const {sendResponse} = require('../helpers/responseHelper'); -const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses') -const {schemas} = require("../models/schemaMapper"); - -const UserAlreadyExistsException = require('../exceptions/userAlreadyExistsError'); -const UserNotExistingError = require("../exceptions/userNotExistingError"); -const ImproperDirStructureError = require("../exceptions/improperDirStructureError"); -const EmailAlreadyInSystemError = require("../exceptions/emailAlreadyInSystemError"); -const {HttpStatusCode} = require("axios"); -const DepartmentAlreadyExistsError = require("../exceptions/departmentAlreadyExistsError"); -const DepartmentNotExistingError = require("../exceptions/departmentNotExistingError"); - const router = express.Router(); - -router.get('/', async (req, res) => { - try { - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], usersJson) - } catch (error) { - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]) - } -}); - -const validateRegisterBody = (req, res, next) => { - const {error, value} = schemas.usersRegisterModelSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; +function validateBody(req, res, next) { next(); } -router.post('/register', validateRegisterBody, async (req, res) => { - try { - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - const {name, email, password, department} = req.jsonModel; - - const existingUser = usersJson.find(user => user.email === email); - if (existingUser) { - throw new UserAlreadyExistsException(); - } - - const userId = uuidv4(); - const hashedPassword = crypto.createHash('sha256').update(password).digest('hex'); - - const newUser = { - id: userId, - name: name, - email: email, - password: hashedPassword, - department: department - }; - - usersJson.push(newUser); - fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2)); - - return sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]); - } catch (error) { - console.error(`Error: ${error.message}`); - - if (error instanceof UserAlreadyExistsException) { - return sendResponse(res, httpStatus.CONFLICT, error.message); - } - - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.post('/register', validateBody, (req, res) => { + const { name, email, password, department } = req.body; }); - -const validateLoginBody = (req, res, next) => { - const {error, value} = schemas.usersLoginModelSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; - next(); -} - -router.post('/login', validateLoginBody, async (req, res) => { - try { - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - const {email, password} = req.jsonModel; - let userIndex = usersJson.findIndex(user => user.email === email); - - if (userIndex === -1) { - throw new UserNotExistingError('Invalid email'); - } - - let hashedPassword = ''; - if(usersJson[userIndex].id === res.locals.ceoID) { - hashedPassword = password; - } - else{ - hashedPassword = crypto.createHash('sha256').update(password).digest('hex'); - } - - console.log(email); - console.log(password); - - userIndex = usersJson.findIndex(user => user.email === email && user.password === hashedPassword); - console.log(userIndex); - if (userIndex === -1) { - throw new UserNotExistingError('Invalid email or password'); - } - - const user = usersJson[userIndex]; - - return sendResponse(res, httpStatus.OK, { - id: user.id, - name: user.name, - email: email, - password: password, - department: user.department - }); - } catch (error) { - console.error(`Error: ${error.message}`); - - if (error instanceof UserNotExistingError) { - return sendResponse(res, httpStatus.UNAUTHORIZED, error.message); - } - - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.post('/login', validateBody, (req, res) => { + const { email, password } = req.body; }); -const validateEmailValidationBody = (req, res, next) => { - const {error, value} = schemas.emailVerificationSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; - next(); -} - -router.post('/validate_email', validateEmailValidationBody, (req, res) => { - try { - const {email} = req.jsonModel; - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - if (usersJson.some(user => user.email === email)) { - throw new EmailAlreadyInSystemError(); - } - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.error(`Error: ${error.message}`); - if (error instanceof EmailAlreadyInSystemError) { - return sendResponse(res, httpStatus.CONFLICT, error.message); - } - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.post('/email-validation', validateBody, (req, res) => { + const { email } = req.body; }); - -const validateDirStructureBody = (req, res, next) => { - const {error, value} = schemas.dirStructureModelSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; - next(); -}; - -router.put('/dir_structure', validateDirStructureBody, (req, res) => { - try { - const {id, dir_config, total_space} = req.jsonModel; - - const backupFilePath = path.join(__dirname, '..', 'db', 'backup_schemas.json'); - let backupArray = []; - if (fs.existsSync(backupFilePath)) { - const backupData = fs.readFileSync(backupFilePath, 'utf8'); - try { - backupArray = JSON.parse(backupData); - } catch (jsonParseError) { - throw new ImproperDirStructureError(); - } - } - - const existingIndex = backupArray.findIndex(item => item.id === id); - if (existingIndex !== -1) { - backupArray[existingIndex] = { - id: id, - dir_config: dir_config, - total_space: total_space - }; - } else { - backupArray.push({ - id: id, - dir_config: dir_config, - total_space: total_space - }); - } - - fs.writeFileSync(backupFilePath, JSON.stringify(backupArray, null, 2)); - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.error(`Error: ${error.message}`); - if (error instanceof ImproperDirStructureError) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.message); - } - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.get('/', (req, res) => { }); -const validateUserDepartmentPatch = (req, res, next) => { - const {error, value} = schemas.userDepartmentPatchSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; - next(); -}; - -router.patch('/:id', validateUserDepartmentPatch, async (req, res) => { - const id = req.params.id; - console.log('esti aici'); - - try { - const department_id = req.jsonModel.department; - - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - const index = usersJson.findIndex(user => user.id === id); - if (index === -1) { - throw new UserNotExistingError(); - } - - const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json'); - const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8'); - const departments = JSON.parse(departmentsData); - - console.log(departments); - - const indexDepartment = departments.findIndex(department => department.id === department_id); - if (indexDepartment === -1) { - throw new DepartmentNotExistingError(); - } - - usersJson[index].department = department_id; - fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2)); - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - }catch(error){ - console.log(error); - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } -}) - - -router.delete('/:id', async (req, res) => { - const id = req.params.id; - - try { - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - const index = usersJson.findIndex(user => user.id === id); - if (index === -1) { - throw new UserNotExistingError(); - } - - usersJson.splice(index, 1); - fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2)); - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.error(`Error: ${error.message}`); - - if (error instanceof UserNotExistingError) { - return sendResponse(res, httpStatus.NOT_FOUND, error.message); - } - - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.put('/', validateBody, (req, res) => { + const { name, email, password } = req.body; }); -const validateModifyUserBody = (req, res, next) => { - const {error, value} = schemas.usersModifyModelSchema.validate(req.body); - if (error) { - return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); - } - req.jsonModel = value; - next(); -}; - -router.put('/:id', validateModifyUserBody, async (req, res) => { - const id = req.params.id; - - try { - const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); - const usersData = fs.readFileSync(usersFilePath, 'utf8'); - const usersJson = JSON.parse(usersData); - - const {name, email, password} = req.jsonModel; - - if (usersJson.some(user => user.email === email && user.id !== id)) { - throw new EmailAlreadyInSystemError(); - } - - let index = usersJson.findIndex(user => user.id === id); - if (index === -1) { - throw new UserNotExistingError(); - } - - const hashedPassword = crypto.createHash('sha256').update(password).digest('hex'); - - usersJson[index] = { - id: id, - name: name, - email: email, - password: hashedPassword - } - - fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2)); - - return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - } catch (error) { - console.error(`Error: ${error.message}`); - - if (error instanceof EmailAlreadyInSystemError) { - return sendResponse(res, httpStatus.CONFLICT, error.message); - } - - if (error instanceof UserNotExistingError) { - return sendResponse(res, httpStatus.NOT_FOUND, error.message); - } - - return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); - } +router.patch('/change-department', validateBody, (req, res) => { + const { id, department } = req.body; }); - -router.use((req, res) => { - return sendResponse(res, httpStatus.NOT_FOUND, "Path not found"); +router.get('/get_decrypt_keys', (req, res) => { }); module.exports = router; diff --git a/UC/frontend/README.md b/UC/frontend/README.md index 9a2a2e4..14e57f5 100644 --- a/UC/frontend/README.md +++ b/UC/frontend/README.md @@ -1,4 +1,31 @@ +General overview +- + For the use of the admin console, start with 'npm run start' the UI frontend. To see/modify the initial login credentials check the /config/config.json file. -WITHOUT SETTING THE CONFIG FILE FOR THE SERVER (API KEY) THE SERVER WILL NOT START! \ No newline at end of file +WITHOUT SETTING THE CONFIG FILE, THE SERVER WILL NOT WORK AS INTENDED! +(click on the 'Reset UC' when first configuring the server to bring the +UC at the initial state) + +Because the client and CEO apps are dependent on the Central Unit (UC), we strongly +suggest you to make backup at a regular interval in case of hardware failing you will +only have to deploy the server with the configurations already existing. + +Important aspects: +- + +- The starting/stopping of the UC is handled by this 'Admin Console'. The server will not +work if the server which servers as the 'Admin Console' is not running. +- The main server will run at the port 5000, so make sure to configure your routers to +enable routing to the server; +- The client and CEO backup system of their applications will need access at ports 5001 and 5002, +so also make sure to open those ports as well; +- If the CEO wants to change the email address linked to the account, his password will be changed +to a low-risk password, so make sure after any release to specify the account information; + +Closing thoughts +- + +We really hope that you will enjoy our application and for any issues, feel free to contact us at +'username@ourdomain.com' for troubleshooting and other problems. \ No newline at end of file diff --git a/UC/frontend/app.js b/UC/frontend/app.js index a5aebc5..62fe764 100644 --- a/UC/frontend/app.js +++ b/UC/frontend/app.js @@ -7,6 +7,26 @@ const cookieParser = require('cookie-parser'); const checkSession = require('./middlewares/checkSession'); +process.on('SIGINT', shutdown); +process.on('SIGTERM', shutdown); + +function shutdown() { + console.log('Received kill signal, shutting down gracefully.'); + const serverProcess = app.get('serverProcess'); + if (serverProcess && typeof serverProcess.kill === 'function') { + console.log('Shutting down server process...'); + serverProcess.kill(); // Send SIGTERM to server process + + serverProcess.on('exit', () => { + console.log('Server process terminated.'); + process.exit(0); // Exit main process cleanly + }); + } else { + console.log('No server process or cannot be killed, exiting.'); + process.exit(0); // Exit main process cleanly + } +} + const app = express() app.set('serverProcess', null); @@ -18,7 +38,7 @@ app.use(bodyParser.json()); app.use(express.static(path.join(__dirname, 'public', 'html'))); app.use('/css', express.static(path.join(__dirname, 'public', 'css'))); app.use('/js', express.static(path.join(__dirname, 'public', 'js'))); -app.use('/images', express.static(path.join(__dirname, 'public', 'assets'))); +app.use('/images', express.static(path.join(__dirname, 'public', 'images'))); app.use(checkSession); @@ -31,3 +51,5 @@ const PORT = process.env.PORT || 80; app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); + + diff --git a/UC/frontend/public/css/login.css b/UC/frontend/public/css/login.css index 0c632ae..ede36f3 100644 --- a/UC/frontend/public/css/login.css +++ b/UC/frontend/public/css/login.css @@ -15,6 +15,8 @@ body, html { } .container { + opacity: 0; + display: flex; flex-direction: row; justify-content: space-evenly; diff --git a/UC/frontend/public/css/main_menu.css b/UC/frontend/public/css/main_menu.css index e5a2278..942ccf5 100644 --- a/UC/frontend/public/css/main_menu.css +++ b/UC/frontend/public/css/main_menu.css @@ -13,78 +13,9 @@ body, html { background-repeat: no-repeat; } - -.overlay { - display: none; - position: fixed; - width: 100%; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.85); - z-index: 2; - cursor: pointer; -} - -.ceo-validation-form { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 300px; - padding: 20px; - background: #1B1A55; - border-radius: 10px; - cursor: default; -} - -.ceo-validation-form h2 { - text-align: center; - color: white; -} - -.form-actions { - text-align: center; - padding-top: 20px; -} - -.form-actions button { - padding: 10px 20px; - margin: 0 10px; - border: none; - border-radius: 5px; - cursor: pointer; -} - -input { - text-align: center; - color: white; - font-weight: bold; - font-size: 1.2rem; - width: 80%; - padding: 1rem; - margin: 0.7rem; - background-color: #1B1A55; - border-radius: 10px; -} - -#back_button { - background-color: #f44336; - width: 35%; - height: auto; - color: white; -} - -#submit_button { - background-color: #4CAF50; - width: 35%; - height: auto; - color: white; -} - .container { + opacity: 0; + display: flex; flex-direction: row; justify-content: space-evenly; diff --git a/UC/frontend/public/css/transition.css b/UC/frontend/public/css/transition.css new file mode 100644 index 0000000..442e6f8 --- /dev/null +++ b/UC/frontend/public/css/transition.css @@ -0,0 +1,25 @@ +.fade-in { + animation: fadeInAnimation 0.5s ease-in forwards; +} + +.fade-out { + animation: fadeOutAnimation 0.5s ease-out forwards; +} + +@keyframes fadeInAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeOutAnimation { + from { + opacity: 1; + } + to { + opacity: 0; + } +} diff --git a/UC/frontend/public/html/login.html b/UC/frontend/public/html/login.html index 9035915..a7fad83 100644 --- a/UC/frontend/public/html/login.html +++ b/UC/frontend/public/html/login.html @@ -5,10 +5,28 @@ + + +