diff --git a/UC/src/db/config.json b/UC/src/db/config.json index 5ed633e..c878440 100644 --- a/UC/src/db/config.json +++ b/UC/src/db/config.json @@ -1,4 +1,4 @@ { - "adminKey": "590dceb42261808e4a804607b38eb119a09c93d84554bac0d23d705d5c6e8ca5", + "adminKey": "9ded8e96a7a03fb7ce23a19b4f6a3d08ac42926aa96664af941e4d02e0b93f0d", "ceoID": "a7635c7a-d6a0-43dc-8e24-552ab33239b8" } \ No newline at end of file diff --git a/UC/src/db/departments.json b/UC/src/db/departments.json index 8c847c1..1a39bce 100644 --- a/UC/src/db/departments.json +++ b/UC/src/db/departments.json @@ -1,7 +1,12 @@ [ { - "id": "f1444987-c0c2-4ce8-89e3-b21f302bff7f", + "id": "16183532-3090-4e24-8def-aa29f316c1c5", "name": "Programatori", - "key": "52a50f932fabf2ba64e337664051804a4bf1e1dcadc548cfe85be92b8c1d470e" + "key": "66238d3abd92e43486a1f8526ff83646a2481b4fb669667daf81ecc71e8211e3" + }, + { + "id": "897b883b-7638-452e-bed9-81e8da2ec0c3", + "name": "Contabili", + "key": "55956d2a1cad9e3952f3756ded721724f71baada95c9addbf46f72defa06d354" } ] \ No newline at end of file diff --git a/UC/src/db/security_levels.json b/UC/src/db/security_levels.json index 64c4767..1889482 100644 --- a/UC/src/db/security_levels.json +++ b/UC/src/db/security_levels.json @@ -1,3 +1,5 @@ { - "1": "f1444987-c0c2-4ce8-89e3-b21f302bff7f" + "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/src/db/users.json b/UC/src/db/users.json index f9240b8..7d6fb6c 100644 --- a/UC/src/db/users.json +++ b/UC/src/db/users.json @@ -6,9 +6,10 @@ "password": "405ee6885be5385223830874bd6dcfca" }, { - "id": "a48e1913-01f5-4df3-a2ba-476e9baa39e4", + "id": "ffc90aca-bb31-4641-9a43-30b343924e8a", "name": "Andrei", - "email": "a@b.com", - "password": "5447045dc3cfaafdb49b365e31dad41eb88616b0b1449930e55fd3a70bd8cee4" + "email": "a@c.com", + "password": "5447045dc3cfaafdb49b365e31dad41eb88616b0b1449930e55fd3a70bd8cee4", + "department": "16183532-3090-4e24-8def-aa29f316c1c5" } ] \ No newline at end of file diff --git a/UC/src/models/schemaMapper.js b/UC/src/models/schemaMapper.js index 067bfb1..06e332f 100644 --- a/UC/src/models/schemaMapper.js +++ b/UC/src/models/schemaMapper.js @@ -5,6 +5,7 @@ const dirStructureModelSchema = require('./dirStructureModel'); const departmentRegisterModelSchema = require('./departmentRegisterModel'); const ceoRegisterModelSchema = require('./ceoRegisterModel'); const emailVerificationSchema = require('./emailVerificationModel'); +const userDepartmentPatchSchema = require('./userDepartmentPatchModel'); module.exports = { schemas: { @@ -14,6 +15,7 @@ module.exports = { usersModifyModelSchema, departmentRegisterModelSchema, ceoRegisterModelSchema, - emailVerificationSchema + emailVerificationSchema, + userDepartmentPatchSchema }, }; \ No newline at end of file diff --git a/UC/src/models/userDepartmentPatchModel.js b/UC/src/models/userDepartmentPatchModel.js new file mode 100644 index 0000000..75e4992 --- /dev/null +++ b/UC/src/models/userDepartmentPatchModel.js @@ -0,0 +1,7 @@ +const Joi = require("joi"); + +const UserDepartmentPatchModel = Joi.object({ + department: Joi.string().uuid().required() +}); + +module.exports = UserDepartmentPatchModel; diff --git a/UC/src/routes/admin.js b/UC/src/routes/admin.js index 6d3c4ce..4467d5a 100644 --- a/UC/src/routes/admin.js +++ b/UC/src/routes/admin.js @@ -117,6 +117,7 @@ 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(); } diff --git a/UC/src/routes/users.js b/UC/src/routes/users.js index c6dee74..9af2a62 100644 --- a/UC/src/routes/users.js +++ b/UC/src/routes/users.js @@ -1,23 +1,25 @@ const express = require('express'); const fs = require('fs'); const path = require('path'); -const { v4: uuidv4, validate} = require('uuid'); +const {v4: uuidv4, validate} = require('uuid'); const crypto = require('crypto'); -const { sendResponse } = require('../helpers/responseHelper'); +const {sendResponse} = require('../helpers/responseHelper'); const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses') const {schemas} = require("../models/schemaMapper"); -const UserAlreadyExistsException =require('../exceptions/userAlreadyExistsError'); +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) => { +router.get('/', async (req, res) => { try { const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); const usersData = fs.readFileSync(usersFilePath, 'utf8'); @@ -30,7 +32,7 @@ router.get('/', async(req, res) => { }); const validateRegisterBody = (req, res, next) => { - const { error, value } = schemas.usersRegisterModelSchema.validate(req.body); + const {error, value} = schemas.usersRegisterModelSchema.validate(req.body); if (error) { return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); } @@ -44,7 +46,7 @@ router.post('/register', validateRegisterBody, async (req, res) => { const usersData = fs.readFileSync(usersFilePath, 'utf8'); const usersJson = JSON.parse(usersData); - const { name, email, password, department} = req.jsonModel; + const {name, email, password, department} = req.jsonModel; const existingUser = usersJson.find(user => user.email === email); if (existingUser) { @@ -79,7 +81,7 @@ router.post('/register', validateRegisterBody, async (req, res) => { const validateLoginBody = (req, res, next) => { - const { error, value } = schemas.usersLoginModelSchema.validate(req.body); + const {error, value} = schemas.usersLoginModelSchema.validate(req.body); if (error) { return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); } @@ -93,7 +95,7 @@ router.post('/login', validateLoginBody, async (req, res) => { const usersData = fs.readFileSync(usersFilePath, 'utf8'); const usersJson = JSON.parse(usersData); - const { email, password } = req.jsonModel; + const {email, password} = req.jsonModel; const hashedPassword = crypto.createHash('sha256').update(password).digest('hex'); const user = usersJson.find(user => user.email === email && user.password === hashedPassword); @@ -120,7 +122,7 @@ router.post('/login', validateLoginBody, async (req, res) => { }); const validateEmailValidationBody = (req, res, next) => { - const { error, value } = schemas.emailVerificationSchema.validate(req.body); + const {error, value} = schemas.emailVerificationSchema.validate(req.body); if (error) { return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); } @@ -140,7 +142,7 @@ router.post('/validate_email', validateEmailValidationBody, (req, res) => { } return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); - }catch(error){ + } catch (error) { console.error(`Error: ${error.message}`); if (error instanceof EmailAlreadyInSystemError) { return sendResponse(res, httpStatus.CONFLICT, error.message); @@ -150,9 +152,8 @@ router.post('/validate_email', validateEmailValidationBody, (req, res) => { }); - const validateDirStructureBody = (req, res, next) => { - const { error, value } = schemas.dirStructureModelSchema.validate(req.body); + const {error, value} = schemas.dirStructureModelSchema.validate(req.body); if (error) { return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); } @@ -162,7 +163,7 @@ const validateDirStructureBody = (req, res, next) => { router.put('/dir_structure', validateDirStructureBody, (req, res) => { try { - const { id, dir_config, total_space } = req.jsonModel; + const {id, dir_config, total_space} = req.jsonModel; const backupFilePath = path.join(__dirname, '..', 'db', 'backup_schemas.json'); let backupArray = []; @@ -202,6 +203,52 @@ router.put('/dir_structure', validateDirStructureBody, (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; @@ -232,7 +279,7 @@ router.delete('/:id', async (req, res) => { }); const validateModifyUserBody = (req, res, next) => { - const { error, value } = schemas.usersModifyModelSchema.validate(req.body); + const {error, value} = schemas.usersModifyModelSchema.validate(req.body); if (error) { return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); } @@ -274,7 +321,7 @@ router.put('/:id', validateModifyUserBody, async (req, res) => { } catch (error) { console.error(`Error: ${error.message}`); - if (error instanceof EmailAlreadyInSystemError){ + if (error instanceof EmailAlreadyInSystemError) { return sendResponse(res, httpStatus.CONFLICT, error.message); } diff --git a/User/loginData.json b/User/loginData.json index 10a50c7..a1fedf8 100644 --- a/User/loginData.json +++ b/User/loginData.json @@ -1,6 +1,7 @@ { - "id": "a48e1913-01f5-4df3-a2ba-476e9baa39e4", + "id": "ffc90aca-bb31-4641-9a43-30b343924e8a", "name": "Andrei", - "email": "a@b.com", - "password": "andreicerbu" + "email": "a@c.com", + "password": "andreicerbu", + "department": "16183532-3090-4e24-8def-aa29f316c1c5" } \ No newline at end of file diff --git a/User/src/main/main.js b/User/src/main/main.js index 974a2a3..9b9cfb0 100644 --- a/User/src/main/main.js +++ b/User/src/main/main.js @@ -167,6 +167,14 @@ ipcMain.handle('open-backup-dir-dialog', async (event) => { } }); +ipcMain.handle('open-file-dialog', async (event) => { + const result = await dialog.showOpenDialog({ + properties: ['openFile'] + }); + + return result.filePaths[0] || ''; +}); + ipcMain.handle('check-file-exists', async (event, fileName) => { try { const filePath = path.join(__dirname, '..', '..', fileName); diff --git a/User/src/main/preload.js b/User/src/main/preload.js index eacfecc..6a718c7 100644 --- a/User/src/main/preload.js +++ b/User/src/main/preload.js @@ -8,5 +8,6 @@ contextBridge.exposeInMainWorld('electronAPI', { showAlert: (message) => ipcRenderer.invoke('show-alert', message), closeAlertWindow: () => ipcRenderer.send('close-alert-window'), openBackupDirDialog: () => ipcRenderer.invoke('open-backup-dir-dialog'), + openFileDialog: () => ipcRenderer.invoke('open-file-dialog'), checkFileExists: (fileName) => ipcRenderer.invoke('check-file-exists', fileName) }); \ No newline at end of file diff --git a/User/src/renderer/css/change_department.css b/User/src/renderer/css/change_department.css index bcc894f..2549504 100644 --- a/User/src/renderer/css/change_department.css +++ b/User/src/renderer/css/change_department.css @@ -22,7 +22,7 @@ body, html { min-height: 100vh; } -.signup-form { +.department-form { background-color: #535C91; opacity: 71; padding: 8vh 3vh 5vh; @@ -31,24 +31,24 @@ body, html { width: 25%; } -.signup-form-title{ +.department-form-title{ margin-bottom: 5vh; color: #FFFFFF; text-align: center; } -.signup-form-title h2 { +.department-form-title h2 { color: #FFFFFF; margin: 0; padding: 0; font-size: 5vh; } -.signup-form-title hr{ +.department-form-title hr{ width: 65%; } -.signup-form-content{ +.department-form-content{ display: flex; margin-left: 2rem; flex-direction: column; @@ -58,11 +58,11 @@ body, html { font-weight: bold; } -.signup-form-content input{ +.department-form-content input{ margin: 0.7rem; } -.signup-form-footer{ +.department-form-footer{ margin-top: 7vh; display: flex; flex-wrap: wrap; @@ -86,12 +86,12 @@ button:hover{ filter: brightness(85%); } -.signup-form button[name="continue"] { +button[name="submit"] { background-color: #F44336; color: white; } -.signup-form button[name="back"] { +button[name="back"] { background-color: #23BDEE; color: white; } \ No newline at end of file diff --git a/User/src/renderer/css/share_file.css b/User/src/renderer/css/share_file.css index 07344a4..a4a77ae 100644 --- a/User/src/renderer/css/share_file.css +++ b/User/src/renderer/css/share_file.css @@ -28,6 +28,10 @@ h1, h2{ margin: 0; } +h2{ + font-size: 1rem; +} + .left_block{ display: flex; flex-direction: column; @@ -120,7 +124,7 @@ button:hover{ flex-direction: column; background-color: #535C91; opacity: 71; - padding: 2rem; + padding: 2rem 4rem 2rem 2rem; border-radius: 1rem; box-shadow: 10px 10px 10px rgba(0, 0, 0, 0.9); color: white; @@ -135,7 +139,7 @@ button:hover{ } .choose_user_form_title hr{ - width: 40%; + width: 80%; } .choose_user_form_content{ diff --git a/User/src/renderer/html/change_department.html b/User/src/renderer/html/change_department.html index 98f2b1d..61c1445 100644 --- a/User/src/renderer/html/change_department.html +++ b/User/src/renderer/html/change_department.html @@ -4,29 +4,29 @@ - Department Selection + + + + + + Department Selection
-
-
- -
+
+ +
diff --git a/User/src/renderer/html/share_file.html b/User/src/renderer/html/share_file.html index 83768d6..fabf9a9 100644 --- a/User/src/renderer/html/share_file.html +++ b/User/src/renderer/html/share_file.html @@ -4,7 +4,11 @@ + + + + Share File @@ -18,36 +22,26 @@

(can be whatever resource from the system)

-

No file chosen

+

- +
-
-
-
-

USERS

-
-
-
- - - - - - - - - -
-
-
+
+
+

USERS

+
+
+
+ +
+
diff --git a/User/src/renderer/js/change_department.js b/User/src/renderer/js/change_department.js new file mode 100644 index 0000000..9a9a035 --- /dev/null +++ b/User/src/renderer/js/change_department.js @@ -0,0 +1,82 @@ +document.addEventListener('DOMContentLoaded', async function() { + try { + // Make API call to fetch department data + const response = await fetch('http://localhost:5000/departments', { + method: 'GET', + headers: { + 'x-api-key': 'uc_api' + } + }); + const res = await response.json(); + const data = res['data']; + + const formContent = document.querySelector('.department-form-content'); + data.forEach(department => { + const label = document.createElement('label'); + label.innerHTML = `${department.name}`; + formContent.appendChild(label); + }); + } catch (error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + } + + document.getElementById('back').addEventListener('click', async function () { + try { + await window.electronAPI.changeContent('main_menu.html'); + console.log('Content changed successfully'); + } catch (error) { + console.error('Error changing content:', error); + } + }); + + document.getElementById('submit').addEventListener('click', async function(e) { + e.preventDefault(); + + const selectedDept = document.querySelector('input[name="dept"]:checked').value; + + try { + if (!selectedDept) { + throw new Error('No department had been selected!.'); + } + + let result = await window.electronAPI.readFile('loginData.json'); + if (!result.success) { + throw new Error('Error reading the file. Please try again later.'); + } + + const data = JSON.parse(result.content); + const id = data.id; + + await fetch(`http://localhost:5000/users/${id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify({ + department: selectedDept, + }) + }).then(async response => { + if(!response.ok){ + await window.electronAPI.showAlert("Internal server error. Try again later!") + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + return; + } + + window.electronAPI.changeContent('main_menu.html') + .then(() => console.log('Content changed successfully')) + .catch(error => console.error('Error changing content:', error)); + }).catch(error => { + console.error(error); + }); + + } catch (error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + } + }); +}); \ No newline at end of file diff --git a/User/src/renderer/js/main_menu.js b/User/src/renderer/js/main_menu.js index 472336f..ab33246 100644 --- a/User/src/renderer/js/main_menu.js +++ b/User/src/renderer/js/main_menu.js @@ -35,22 +35,63 @@ document.addEventListener('DOMContentLoaded', async function () { ceoSubmitButton.addEventListener('click', async function(event) { event.preventDefault(); const password = document.getElementById('ceo_password').value; - const ceoPassword = getCeoPassword(); + if (password === '') { - alert('Please enter a password.'); + await window.electronAPI.showAlert('Please enter password') + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error)); return; } - if(password !== ceoPassword){ + let fetchResult; + let fetchJson = undefined; + + fetchResult = await fetch('http://localhost:5000/admin/reset_key', { + method: 'GET', + headers: { + 'x-api-key': 'uc_api' + } + }) + + if(!fetchResult.ok){ + await window.electronAPI.showAlert('Internal server error. Please try again later!') + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error)); + return; + } + + fetchJson = await fetchResult.json(); + const adminKey = fetchJson['data']; + + fetchResult = await fetch('http://localhost:5000/admin/validate_admin_password', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api', + 'admin-key': adminKey + }, + body: JSON.stringify({ + password: password + }) + }) + + if(!fetchResult.ok){ + await window.electronAPI.showAlert('The password is incorrect') + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error)); return; } if (triggerSource === 'change_department') { + window.electronAPI.changeContent('change_department.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); } else if (triggerSource === 'decrypt') { await decryptFiles() } overlay.style.display = 'none'; + triggerSource = ''; }); checkFileExists() @@ -132,8 +173,6 @@ document.addEventListener('DOMContentLoaded', async function () { const userData = await window.electronAPI.readFile('loginData.json'); const userJson = JSON.parse(userData.content); const username = userJson.name; - console.log(userData); - console.log(username); const usernameField = document.getElementById('username_field'); if (usernameField) { diff --git a/User/src/renderer/js/share_file.js b/User/src/renderer/js/share_file.js new file mode 100644 index 0000000..4a64652 --- /dev/null +++ b/User/src/renderer/js/share_file.js @@ -0,0 +1,104 @@ +document.addEventListener("DOMContentLoaded", function() { + let pathToFile = ''; + + function updateFileName() { + const fileNameElement = document.getElementById('fileName'); + if (pathToFile.trim() === '') { + fileNameElement.textContent = 'No file chosen'; + } else { + fileNameElement.textContent = pathToFile.split('\\').pop().split('/').pop(); + } + } + + // Call the function to update file name on DOMContentLoaded + updateFileName(); + + async function fetchUsersAndCreateCheckboxes() { + const result = await window.electronAPI.readFile('loginData.json'); + const loginData = JSON.parse(result.content); + + const {id} = loginData; + + fetch('http://localhost:5000/users', { + method: 'GET', + headers: { + 'x-api-key': 'uc_api' + } + }) + .then(response => response.json()) + .then(data => { + const usersDiv = document.querySelector('.choose_user_form_content'); + usersDiv.innerHTML = ''; + data['data'].forEach(user => { + if (user.id !== id) { + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.name = 'users'; + checkbox.value = user.id; + + const label = document.createElement('label'); + label.innerHTML = `${user.name}`; + label.insertBefore(checkbox, label.firstChild); // Insert checkbox before the label's first child + + usersDiv.appendChild(label); + } + }); + }) + .catch(error => console.error('Error fetching users:', error)); + } + + document.getElementById('selectFile').addEventListener('click', async function () { + console.log('Select file button clicked'); + + try { + pathToFile = await window.electronAPI.openFileDialog(); + updateFileName(); + } catch (error) { + console.error('Error opening file dialog:', error); + } + }); + + document.getElementById('backButton').addEventListener('click', async function () { + console.log('Back button clicked'); + + try { + await window.electronAPI.changeContent('main_menu.html'); + console.log('Content changed successfully'); + } catch (error) { + console.error('Error changing content:', error); + } + }); + + document.getElementById('submitButton').addEventListener('click', async function (event) { + event.preventDefault(); + console.log('Submit button clicked'); + + if (pathToFile === '' || pathToFile.length === 0) { + await window.electronAPI.showAlert('File not chosen!') + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + return + } + + const form = document.getElementById('userDestForm'); + const checkboxes = form.querySelectorAll('input[name="users"]'); + const selectedUserIds = []; + + checkboxes.forEach(checkbox => { + if (checkbox.checked) { + selectedUserIds.push(checkbox.value); + } + }); + + if(selectedUserIds === []){ + await window.electronAPI.showAlert('No user selected!') + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + return; + } + + //TODO: logic to send files + }); + + fetchUsersAndCreateCheckboxes().then(() => console.log('Page rendered')) +});