ran 'npx standard --fix'

This commit is contained in:
xfarrow
2024-02-22 17:30:31 +01:00
parent 42104ac0f4
commit 0dc67edc9b
12 changed files with 564 additions and 596 deletions

View File

@ -13,54 +13,53 @@
// Importing modules // Importing modules
// TODO: clean up // TODO: clean up
require('dotenv').config(); require('dotenv').config()
const express = require('express'); const express = require('express')
const cors = require('cors'); const cors = require('cors')
const rateLimit = require('express-rate-limit'); const rateLimit = require('express-rate-limit')
const personRoutes = require('./routes/person_routes.js'); const personRoutes = require('./routes/person_routes.js')
const organizationRoutes = require('./routes/organization_routes.js'); const organizationRoutes = require('./routes/organization_routes.js')
const organizationAdminRoutes = require('./routes/organization_admin_routes.js'); const organizationAdminRoutes = require('./routes/organization_admin_routes.js')
const organizationPostRoutes = require('./routes/organization_post_routes.js') const organizationPostRoutes = require('./routes/organization_post_routes.js')
const jwt_utils = require('./utils/jwt_utils.js'); const jwt_utils = require('./utils/jwt_utils.js')
// Application configuration // Application configuration
const app = express(); const app = express()
app.use(express.json()); // Middleware which parses JSON for POST requests app.use(express.json()) // Middleware which parses JSON for POST requests
app.use(cors()); // Enable CORS for all routes app.use(cors()) // Enable CORS for all routes
app.use(rateLimit({ app.use(rateLimit({
windowMs: process.env.LIMITER_WINDOW, windowMs: process.env.LIMITER_WINDOW,
max: process.env.LIMITER_MAXIMUM_PER_WINDOW, max: process.env.LIMITER_MAXIMUM_PER_WINDOW,
message: {error : "Too many requests from this IP, please try again later"} message: { error: 'Too many requests from this IP, please try again later' }
})); // Apply the rate limiter middleware to all routes })) // Apply the rate limiter middleware to all routes
const publicRoutes = express.Router(); const publicRoutes = express.Router()
publicRoutes.post('/register', personRoutes.registerPerson); publicRoutes.post('/register', personRoutes.registerPerson)
publicRoutes.post('/login', personRoutes.login); publicRoutes.post('/login', personRoutes.login)
const protectedRoutes = express.Router(); const protectedRoutes = express.Router()
protectedRoutes.use(jwt_utils.verifyToken); protectedRoutes.use(jwt_utils.verifyToken)
protectedRoutes.get('/person/myself', personRoutes.getMyself); protectedRoutes.get('/person/myself', personRoutes.getMyself)
protectedRoutes.get('/person/:id', personRoutes.getPerson); protectedRoutes.get('/person/:id', personRoutes.getPerson)
protectedRoutes.put('/person/:id', personRoutes.updatePerson); protectedRoutes.put('/person/:id', personRoutes.updatePerson)
protectedRoutes.delete('/person/delete', personRoutes.deletePerson); protectedRoutes.delete('/person/delete', personRoutes.deletePerson)
protectedRoutes.post('/organization/admin', organizationAdminRoutes.addOrganizationAdmin); protectedRoutes.post('/organization/admin', organizationAdminRoutes.addOrganizationAdmin)
protectedRoutes.delete('/organization/removeadmin', organizationAdminRoutes.removeOrganizationAdmin); protectedRoutes.delete('/organization/removeadmin', organizationAdminRoutes.removeOrganizationAdmin)
protectedRoutes.post('/organization', organizationRoutes.createOrganization); protectedRoutes.post('/organization', organizationRoutes.createOrganization)
protectedRoutes.get('/organization/:id', organizationRoutes.getOrganization); protectedRoutes.get('/organization/:id', organizationRoutes.getOrganization)
protectedRoutes.put('/organization/:id', organizationRoutes.updateOrganization); protectedRoutes.put('/organization/:id', organizationRoutes.updateOrganization)
protectedRoutes.delete('/organization/:id', organizationRoutes.deleteOrganization); protectedRoutes.delete('/organization/:id', organizationRoutes.deleteOrganization)
protectedRoutes.post('/organization/post', organizationPostRoutes.createOrganizationPost); protectedRoutes.post('/organization/post', organizationPostRoutes.createOrganizationPost)
protectedRoutes.delete('/organization/post/:id', organizationPostRoutes.deleteOrganizationPost); protectedRoutes.delete('/organization/post/:id', organizationPostRoutes.deleteOrganizationPost)
// Mounting routes // Mounting routes
app.use('/api', publicRoutes); // Routes not requiring token app.use('/api', publicRoutes) // Routes not requiring token
app.use('/api', protectedRoutes); // Routes requiring token app.use('/api', protectedRoutes) // Routes requiring token
// Start the server. Default port is 3000 // Start the server. Default port is 3000
const port = process.env.API_SERVER_PORT || 3000; const port = process.env.API_SERVER_PORT || 3000
app.listen(port, () => { app.listen(port, () => {
console.log(`Blink API server is running on port ${port}`); console.log(`Blink API server is running on port ${port}`)
}); })
module.exports = app; module.exports = app

View File

@ -11,7 +11,7 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config'); const knex = require('../utils/knex_config')
/** /**
* Create Organization object * Create Organization object
@ -23,12 +23,12 @@ const knex = require('../utils/knex_config');
*/ */
function organization (name, location, description, is_hiring) { function organization (name, location, description, is_hiring) {
const organization = { const organization = {
name: name, name,
location: location, location,
description: description, description,
is_hiring: is_hiring is_hiring
}; }
return organization; return organization
} }
/** /**
@ -40,8 +40,8 @@ async function getOrganizationById(id){
const organization = await knex('Organization') const organization = await knex('Organization')
.where('id', id) .where('id', id)
.select('*') .select('*')
.first(); .first()
return organization; return organization
} }
/** /**
@ -53,15 +53,15 @@ async function insertOrganization(organization, organizationAdministratorId){
// We have to insert either both in Organization and in OrganizationAdministrator // We have to insert either both in Organization and in OrganizationAdministrator
// or in neither // or in neither
const organizationResult = await trx('Organization') const organizationResult = await trx('Organization')
.insert(organization, '*'); .insert(organization, '*')
// Inserting in the "OrganizationAdministrator" table // Inserting in the "OrganizationAdministrator" table
await trx('OrganizationAdministrator') await trx('OrganizationAdministrator')
.insert({ .insert({
id_person: organizationAdministratorId, id_person: organizationAdministratorId,
id_organization: organizationResult[0].id, id_organization: organizationResult[0].id
}); })
}); })
} }
/** /**
@ -110,8 +110,8 @@ async function updateOrganizationIfAdministrator(organization, organizationId, p
.where('id_person', personId) .where('id_person', personId)
.where('id_organization', organizationId) .where('id_organization', organizationId)
}) })
.update(organization); .update(organization)
return numberOfUpdatedRows == 1; return numberOfUpdatedRows == 1
} }
/** /**
@ -130,8 +130,8 @@ async function deleteOrganizationIfAdmin(organizationId, personId){
.where('id_person', personId) .where('id_person', personId)
.where('id_organization', organizationId) .where('id_organization', organizationId)
}) })
.del(); .del()
return numberOfDeletedRows == 1; return numberOfDeletedRows == 1
} }
// Exporting a function // Exporting a function
@ -144,4 +144,4 @@ module.exports = {
updateOrganizationIfAdministrator, updateOrganizationIfAdministrator,
updateOrganizationIfAdministrator, updateOrganizationIfAdministrator,
deleteOrganizationIfAdmin deleteOrganizationIfAdmin
}; }

View File

@ -11,8 +11,8 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config'); const knex = require('../utils/knex_config')
const bcrypt = require('bcrypt'); const bcrypt = require('bcrypt')
/** /**
* Creates Person object by the specified fields * Creates Person object by the specified fields
@ -28,14 +28,14 @@ const bcrypt = require('bcrypt');
function person (email, password, display_name, date_of_birth, available, enabled, place_of_living) { function person (email, password, display_name, date_of_birth, available, enabled, place_of_living) {
const person = { const person = {
email: email.toLowerCase(), email: email.toLowerCase(),
password: password, password,
display_name: display_name, display_name,
date_of_birth: date_of_birth, date_of_birth,
available: available, available,
enabled: enabled, enabled,
place_of_living: place_of_living place_of_living
}; }
return person; return person
} }
/** /**
@ -46,7 +46,7 @@ function person(email, password, display_name, date_of_birth, available, enabled
async function getPersonByEmail (email) { async function getPersonByEmail (email) {
return await knex('Person') return await knex('Person')
.where('email', email.toLowerCase()) .where('email', email.toLowerCase())
.first(); .first()
} }
/** /**
@ -57,8 +57,8 @@ async function getPersonByEmail(email){
async function getPersonById (id) { async function getPersonById (id) {
return await knex('Person') return await knex('Person')
.select('*') .select('*')
.where({ id: id }) .where({ id })
.first(); .first()
} }
/** /**
@ -81,13 +81,13 @@ async function registerPerson(person, activationLink){
enabled: person.enabled, enabled: person.enabled,
place_of_living: person.place_of_living place_of_living: person.place_of_living
}) })
.returning("id"); .returning('id')
await tr('ActivationLink') await tr('ActivationLink')
.insert({ .insert({
person_id: personIdResult[0].id, person_id: personIdResult[0].id,
identifier: activationLink identifier: activationLink
}); })
}); })
} }
/** /**
@ -102,15 +102,15 @@ async function getPersonByEmailAndPassword(email, password){
.where('email', email.toLowerCase()) .where('email', email.toLowerCase())
.where('enabled', true) .where('enabled', true)
.select('*') .select('*')
.first(); .first()
if (person) { if (person) {
const passwordMatches = await bcrypt.compare(password, person.password); const passwordMatches = await bcrypt.compare(password, person.password)
if (passwordMatches) { if (passwordMatches) {
return person; return person
} }
} }
return null; return null
} }
/** /**
@ -121,7 +121,7 @@ async function getPersonByEmailAndPassword(email, password){
async function updatePerson (person, person_id) { async function updatePerson (person, person_id) {
await knex('Person') await knex('Person')
.where('id', person_id) .where('id', person_id)
.update(person); .update(person)
} }
/** /**
@ -131,10 +131,9 @@ async function updatePerson(person, person_id){
async function deletePerson (person_id) { async function deletePerson (person_id) {
await knex('Person') await knex('Person')
.where({ id: person_id }) .where({ id: person_id })
.del(); .del()
} }
// Exporting a function // Exporting a function
// means making a JavaScript function defined in one // means making a JavaScript function defined in one
// module available for use in another module. // module available for use in another module.
@ -146,4 +145,4 @@ module.exports = {
registerPerson, registerPerson,
updatePerson, updatePerson,
deletePerson deletePerson
}; }

View File

@ -11,8 +11,8 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config'); const knex = require('../utils/knex_config')
const organization_admin_model = require('../models/organization_admin_model'); const organization_admin_model = require('../models/organization_admin_model')
/** /**
* POST Method * POST Method
@ -23,24 +23,22 @@ const organization_admin_model = require('../models/organization_admin_model');
* Required field(s): organization_id, person_id * Required field(s): organization_id, person_id
*/ */
async function addOrganizationAdmin (req, res) { async function addOrganizationAdmin (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.person_id) { if (!req.body.organization_id || !req.body.person_id) {
return res.status(400).json({ error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' })
} }
try { try {
const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id); const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id)
// TOC/TOU // TOC/TOU
if (!isPersonAdmin) { if (!isPersonAdmin) {
return res.status(401).json({error : "Forbidden"}); return res.status(401).json({ error: 'Forbidden' })
} }
await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id); await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id)
return res.status(200).json({success : true}); return res.status(200).json({ success: true })
} } catch (error) {
catch (error) { console.error('Error while adding organization admin: ' + error)
console.error('Error while adding organization admin: ' + error); res.status(500).json({ error: 'Internal server error' })
res.status(500).json({error : "Internal server error"});
} }
} }
@ -54,23 +52,21 @@ async function addOrganizationAdmin(req, res){
* Required field(s): organization_id * Required field(s): organization_id
*/ */
async function removeOrganizationAdmin (req, res) { async function removeOrganizationAdmin (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.organization_id) { if (!req.body.organization_id) {
return res.status(400).json({ error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' })
} }
try { try {
await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id); await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id)
return res.status(200).json({success : true}); return res.status(200).json({ success: true })
} } catch (error) {
catch (error){ console.error(error)
console.error(error); return res.status(500).json({ error: 'Internal server error' })
return res.status(500).json({ error: "Internal server error"});
} }
} }
module.exports = { module.exports = {
addOrganizationAdmin, addOrganizationAdmin,
removeOrganizationAdmin removeOrganizationAdmin
}; }

View File

@ -11,7 +11,7 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config'); const knex = require('../utils/knex_config')
/** /**
* POST Request * POST Request
@ -22,10 +22,9 @@ const knex = require('../utils/knex_config');
* @returns the inserted Post * @returns the inserted Post
*/ */
async function createOrganizationPost (req, res) { async function createOrganizationPost (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.content) { if (!req.body.organization_id || !req.body.content) {
return res.status(400).json({ error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' })
} }
try { try {
@ -34,12 +33,12 @@ async function createOrganizationPost(req, res){
.where('id_person', req.jwt.person_id) .where('id_person', req.jwt.person_id)
.where('id_organization', req.body.organization_id) .where('id_organization', req.body.organization_id)
.select('*') .select('*')
.first(); .first()
// Non-exploitable TOC/TOU weakness // Non-exploitable TOC/TOU weakness
// For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat // For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat
if (!isOrganizationAdmin) { if (!isOrganizationAdmin) {
return res.status(403).json({error : "Forbidden"}); return res.status(403).json({ error: 'Forbidden' })
} }
const organizationPost = await knex('OrganizationPost') const organizationPost = await knex('OrganizationPost')
@ -48,12 +47,11 @@ async function createOrganizationPost(req, res){
content: req.body.content, content: req.body.content,
original_author: req.jwt.person_id original_author: req.jwt.person_id
}) })
.returning('*'); .returning('*')
return res.status(200).json(organizationPost[0]); return res.status(200).json(organizationPost[0])
} } catch (error) {
catch (error) { console.log('Error while creating Organization Post: ' + error)
console.log("Error while creating Organization Post: " + error); return res.status(500).json({ error: 'Internal server error' })
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -66,8 +64,7 @@ async function createOrganizationPost(req, res){
* Required field(s): none. * Required field(s): none.
*/ */
async function deleteOrganizationPost (req, res) { async function deleteOrganizationPost (req, res) {
const organizationPostIdToDelete = req.params.id
const organizationPostIdToDelete = req.params.id;
try { try {
const isOrganizationAdmin = await knex('OrganizationPost') const isOrganizationAdmin = await knex('OrganizationPost')
@ -75,22 +72,20 @@ async function deleteOrganizationPost(req, res){
.where('OrganizationPost.id', organizationPostIdToDelete) .where('OrganizationPost.id', organizationPostIdToDelete)
.where('OrganizationAdministrator.id_person', req.jwt.person_id) .where('OrganizationAdministrator.id_person', req.jwt.person_id)
.select('*') .select('*')
.first(); .first()
// Unexploitable TOC/TOU // Unexploitable TOC/TOU
if (isOrganizationAdmin) { if (isOrganizationAdmin) {
await knex('OrganizationPost') await knex('OrganizationPost')
.where('id', organizationPostIdToDelete) .where('id', organizationPostIdToDelete)
.del(); .del()
return res.status(200).json({success : true}); return res.status(200).json({ success: true })
} else {
return res.status(401).json({ error: 'Forbidden' })
} }
else{ } catch (error) {
return res.status(401).json({error : "Forbidden"}); console.log(error)
} res.status(500).json({ error: 'Internal server error' })
}
catch (error) {
console.log(error);
res.status(500).json({error : "Internal server error"});
} }
} }
@ -100,4 +95,4 @@ async function deleteOrganizationPost(req, res){
module.exports = { module.exports = {
createOrganizationPost, createOrganizationPost,
deleteOrganizationPost deleteOrganizationPost
}; }

View File

@ -11,7 +11,7 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const organization_model = require('../models/organization_model'); const organization_model = require('../models/organization_model')
/** /**
* POST Request * POST Request
@ -23,20 +23,18 @@ const organization_model = require('../models/organization_model');
* @returns the inserted organization * @returns the inserted organization
*/ */
async function createOrganization (req, res) { async function createOrganization (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.name) { if (!req.body.name) {
return res.status(400).json({ error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' })
} }
try { try {
const organization = organization_model.organization(req.body.name, req.body.location, req.body.description, req.body.is_hiring); const organization = organization_model.organization(req.body.name, req.body.location, req.body.description, req.body.is_hiring)
await organization_model.insertOrganization(organization, req.jwt.person_id); await organization_model.insertOrganization(organization, req.jwt.person_id)
return res.status(200).json({ Organization: organization }); return res.status(200).json({ Organization: organization })
} } catch (error) {
catch (error){ console.error('Error creating Organization:', error)
console.error('Error creating Organization:', error); res.status(500).json({ error: 'Internal server error' })
res.status(500).json({error : "Internal server error"});
} }
} }
@ -47,41 +45,38 @@ async function createOrganization(req, res){
* Required field(s): none. * Required field(s): none.
*/ */
async function updateOrganization (req, res) { async function updateOrganization (req, res) {
const updateOrganization = {}
const updateOrganization = {};
if (req.body.name) { if (req.body.name) {
updateOrganization.name = req.body.name; updateOrganization.name = req.body.name
} }
if (req.body.location) { if (req.body.location) {
updateOrganization.location = req.body.location; updateOrganization.location = req.body.location
} }
if (req.body.description) { if (req.body.description) {
updateOrganization.description = req.body.description; updateOrganization.description = req.body.description
} }
if (req.body.is_hiring) { if (req.body.is_hiring) {
updateOrganization.is_hiring = req.body.is_hiring; updateOrganization.is_hiring = req.body.is_hiring
} }
if (Object.keys(updateOrganization).length === 0) { if (Object.keys(updateOrganization).length === 0) {
return res.status(400).json({ error : "Bad request. No data to update"}); return res.status(400).json({ error: 'Bad request. No data to update' })
} }
try { try {
const isUpdateSuccessful = organization_model.updateOrganizationIfAdministrator(updateOrganization, req.params.id, req.jwt.person_id); const isUpdateSuccessful = organization_model.updateOrganizationIfAdministrator(updateOrganization, req.params.id, req.jwt.person_id)
if (isUpdateSuccessful) { if (isUpdateSuccessful) {
return res.status(200).json({ success : "true"}); return res.status(200).json({ success: 'true' })
} else {
return res.status(404).json({ error: 'Organization either not found or insufficient permissions' })
} }
else{ } catch (error) {
return res.status(404).json({error : "Organization either not found or insufficient permissions"}); console.log(error)
} return res.status(500).json({ error: 'Internal server error' })
}
catch (error) {
console.log(error);
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -93,17 +88,15 @@ async function updateOrganization(req, res){
*/ */
async function deleteOrganization (req, res) { async function deleteOrganization (req, res) {
try { try {
const isDeleteSuccessful = organization_model.deleteOrganizationIfAdmin(req.params.id, req.jwt.person_id); const isDeleteSuccessful = organization_model.deleteOrganizationIfAdmin(req.params.id, req.jwt.person_id)
if (isDeleteSuccessful) { if (isDeleteSuccessful) {
return res.status(403).json({error: "Forbidden"}); return res.status(403).json({ error: 'Forbidden' })
} else {
return res.status(200).json({ success: true })
} }
else{ } catch (error) {
return res.status(200).json({success: true}); console.error(error)
} return res.status(500).json({ error: 'Internal server error' })
}
catch (error) {
console.error(error);
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -118,17 +111,15 @@ async function deleteOrganization(req, res){
*/ */
async function getOrganization (req, res) { async function getOrganization (req, res) {
try { try {
const organization = await organization_model.getOrganizationById(req.params.id); const organization = await organization_model.getOrganizationById(req.params.id)
if (organization) { if (organization) {
return res.status(200).json(organization); return res.status(200).json(organization)
} else {
return res.status(404).json({ error: 'Not found' })
} }
else{ } catch (error) {
return res.status(404).json({error : "Not found"}); console.error('Error retrieving an organization: ' + error)
} return res.status(500).json({ error: 'Internal server error' })
}
catch (error) {
console.error("Error retrieving an organization: " + error);
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -137,5 +128,4 @@ module.exports = {
getOrganization, getOrganization,
updateOrganization, updateOrganization,
deleteOrganization deleteOrganization
}; }

View File

@ -11,12 +11,12 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const validator = require('../utils/validation'); const validator = require('../utils/validation')
const knex = require('../utils/knex_config'); const knex = require('../utils/knex_config')
const jwt_utils = require('../utils/jwt_utils'); const jwt_utils = require('../utils/jwt_utils')
const bcrypt = require('bcrypt'); const bcrypt = require('bcrypt')
const crypto = require('crypto'); const crypto = require('crypto')
const person_model = require('../models/person_model'); const person_model = require('../models/person_model')
/** /**
* POST Request * POST Request
@ -28,29 +28,28 @@ const person_model = require('../models/person_model');
* @returns The activationlink identifier * @returns The activationlink identifier
*/ */
async function registerPerson (req, res) { async function registerPerson (req, res) {
// Does this server allow users to register? // Does this server allow users to register?
if (process.env.ALLOW_USER_REGISTRATION === 'false') { if (process.env.ALLOW_USER_REGISTRATION === 'false') {
return res.status(403).json({error : "Users cannot register on this server"}); return res.status(403).json({ error: 'Users cannot register on this server' })
} }
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.display_name || !req.body.email || !req.body.password) { if (!req.body.display_name || !req.body.email || !req.body.password) {
return res.status(400).json({ error : "Some or all required fields are missing"}); return res.status(400).json({ error: 'Some or all required fields are missing' })
} }
if (!validator.validateEmail(req.body.email)) { if (!validator.validateEmail(req.body.email)) {
return res.status(400).json({ error : "The email is not in a valid format"}); return res.status(400).json({ error: 'The email is not in a valid format' })
} }
// Generate activation link token // Generate activation link token
const activationLink = crypto.randomBytes(16).toString('hex'); const activationLink = crypto.randomBytes(16).toString('hex')
// Hash provided password // Hash provided password
const hashPasswordPromise = bcrypt.hash(req.body.password, 10); const hashPasswordPromise = bcrypt.hash(req.body.password, 10)
try { try {
// Check whether e-mail exists already (enforced by database constraints) // Check whether e-mail exists already (enforced by database constraints)
const existingUser = await person_model.getPersonByEmail(req.body.email); const existingUser = await person_model.getPersonByEmail(req.body.email)
if (existingUser) { if (existingUser) {
return res.status(409).json({ error: "E-mail already in use" }); return res.status(409).json({ error: 'E-mail already in use' })
} }
const personToInsert = person_model.person( const personToInsert = person_model.person(
req.body.email, req.body.email,
@ -59,13 +58,12 @@ async function registerPerson(req, res){
req.body.date_of_birth, req.body.date_of_birth,
req.body.available, req.body.available,
true, true,
req.body.place_of_living); req.body.place_of_living)
await person_model.registerPerson(personToInsert, activationLink); await person_model.registerPerson(personToInsert, activationLink)
return res.status(200).json({ activationLink: activationLink }); return res.status(200).json({ activationLink })
} } catch (error) {
catch (error){ console.error('Error registering person:', error)
console.error('Error registering person:', error); res.status(500).json({ error: 'Internal server error' })
res.status(500).json({error : "Internal server error"});
} }
} }
@ -80,24 +78,22 @@ async function registerPerson(req, res){
* @returns The token * @returns The token
*/ */
async function login (req, res) { async function login (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.email || !req.body.password) { if (!req.body.email || !req.body.password) {
return res.status(400).json({error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' })
} }
try { try {
const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password); const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password)
if (person) { if (person) {
const token = jwt_utils.generateToken(person.id); const token = jwt_utils.generateToken(person.id)
res.status(200).json({token: token }); res.status(200).json({ token })
} } else {
else{ res.status(401).json({ error: 'Unauthorized' })
res.status(401).json({error : "Unauthorized"});
} }
} catch (error) { } catch (error) {
console.error('Error logging in: ', error); console.error('Error logging in: ', error)
res.status(500).json({error : "Internal server error"}); res.status(500).json({ error: 'Internal server error' })
} }
} }
@ -112,19 +108,18 @@ async function login(req, res){
*/ */
async function getPerson (req, res) { async function getPerson (req, res) {
try { try {
const person = await person_model.getPersonById(req.params.id); const person = await person_model.getPersonById(req.params.id)
if (person) { if (person) {
// I am retrieving either myself or an enabled user // I am retrieving either myself or an enabled user
if (person.id == req.jwt.person_id || person.enabled) { if (person.id == req.jwt.person_id || person.enabled) {
delete person['password']; // remove password field for security reasons delete person.password // remove password field for security reasons
return res.status(200).send(person); return res.status(200).send(person)
} }
} }
return res.status(404).json({error: "Not found"}); return res.status(404).json({ error: 'Not found' })
} } catch (error) {
catch (error) { console.log('Error while getting person: ' + error)
console.log("Error while getting person: " + error); return res.status(500).json({ error: 'Internal server error' })
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -138,16 +133,15 @@ async function getPerson(req, res){
*/ */
async function getMyself (req, res) { async function getMyself (req, res) {
try { try {
const person = await person_model.getPersonById(req.jwt.person_id); const person = await person_model.getPersonById(req.jwt.person_id)
if (person) { if (person) {
delete person['password']; delete person.password
return res.status(200).send(person); return res.status(200).send(person)
} }
return res.status(404).json({error: "Not found"}); return res.status(404).json({ error: 'Not found' })
} } catch (error) {
catch (error){ console.log('Error while getting myself: ' + error)
console.log("Error while getting myself: " + error); return res.status(500).json({ error: 'Internal server error' })
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -162,32 +156,30 @@ async function getMyself(req, res){
* *
*/ */
async function updatePerson (req, res) { async function updatePerson (req, res) {
if (req.jwt.person_id != req.params.id) { if (req.jwt.person_id != req.params.id) {
return res.status(403).json({ error : "Forbidden"}); return res.status(403).json({ error: 'Forbidden' })
} }
const updatePerson = {}; const updatePerson = {}
if (req.body.display_name) { if (req.body.display_name) {
updatePerson.display_name = req.body.display_name; updatePerson.display_name = req.body.display_name
} }
if (req.body.date_of_birth) { if (req.body.date_of_birth) {
if (validator.isPostgresDateFormatValid(req.body.date_of_birth)) { if (validator.isPostgresDateFormatValid(req.body.date_of_birth)) {
updatePerson.date_of_birth = req.body.date_of_birth; updatePerson.date_of_birth = req.body.date_of_birth
} } else {
else{ return res.status(400).json({ error: 'Date of birth format not valid. Please specify a YYYY-MM-DD date' })
return res.status(400).json({ error : "Date of birth format not valid. Please specify a YYYY-MM-DD date"});
} }
} }
if (req.body.available) { if (req.body.available) {
updatePerson.available = req.body.available; updatePerson.available = req.body.available
} }
if (req.body.place_of_living) { if (req.body.place_of_living) {
updatePerson.place_of_living = req.body.place_of_living; updatePerson.place_of_living = req.body.place_of_living
} }
// If we are tying to change password, the old password must be provided // If we are tying to change password, the old password must be provided
@ -195,27 +187,25 @@ async function updatePerson(req, res){
const user = await knex('Person') const user = await knex('Person')
.select('password') .select('password')
.where({ id: req.jwt.person_id }) .where({ id: req.jwt.person_id })
.first(); .first()
const passwordMatches = await bcrypt.compare(req.body.old_password, user.password); const passwordMatches = await bcrypt.compare(req.body.old_password, user.password)
if (passwordMatches) { if (passwordMatches) {
updatePerson.password = await bcrypt.hash(req.body.new_password, 10); updatePerson.password = await bcrypt.hash(req.body.new_password, 10)
} } else {
else{ return res.status(401).json({ error: 'Password verification failed' })
return res.status(401).json({ error : "Password verification failed"});
} }
} }
if (Object.keys(updatePerson).length === 0) { if (Object.keys(updatePerson).length === 0) {
return res.status(400).json({ error : "Bad request. No data to update"}); return res.status(400).json({ error: 'Bad request. No data to update' })
} }
try { try {
await person_model.updatePerson(updatePerson, req.params.id); await person_model.updatePerson(updatePerson, req.params.id)
return res.status(200).json({ success : "true"}); return res.status(200).json({ success: 'true' })
} } catch (error) {
catch (error) { console.log('Error while updating a Person: ' + error)
console.log("Error while updating a Person: " + error); return res.status(500).json({ error: 'Internal server error' })
return res.status(500).json({ error : "Internal server error"});
} }
} }
@ -231,12 +221,11 @@ async function updatePerson(req, res){
async function deletePerson (req, res) { async function deletePerson (req, res) {
// TODO: Delete Organization if this user was its only administrator // TODO: Delete Organization if this user was its only administrator
try { try {
await person_model.deletePerson(req.jwt.person_id); await person_model.deletePerson(req.jwt.person_id)
return res.status(200).json({success: true}); return res.status(200).json({ success: true })
} } catch (error) {
catch (error) { console.log('Error deleting a Person: ' + error)
console.log("Error deleting a Person: " + error); return res.status(500).json({ error: 'Internal server error' })
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -250,4 +239,4 @@ module.exports = {
getMyself, getMyself,
updatePerson, updatePerson,
deletePerson deletePerson
}; }

View File

@ -11,41 +11,41 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken')
function generateToken (person_id) { function generateToken (person_id) {
// The payload the JWT will carry within itself // The payload the JWT will carry within itself
const payload = { const payload = {
person_id: person_id person_id
}; }
const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, { const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, {
expiresIn: '8h' expiresIn: '8h'
}); })
return token; return token
} }
// Middlware // Middlware
function verifyToken (req, res, next) { function verifyToken (req, res, next) {
const token = req.headers.authorization; const token = req.headers.authorization
if (!token) { if (!token) {
return res.status(401).send({error : 'No token provided'}); return res.status(401).send({ error: 'No token provided' })
} }
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => { jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
if (err) { if (err) {
return res.status(401).send({error : 'Failed to authenticate token'}); return res.status(401).send({ error: 'Failed to authenticate token' })
} }
// If the token is valid, store the decoded data in the request object // If the token is valid, store the decoded data in the request object
// req.jwt will contain the payload created in generateToken // req.jwt will contain the payload created in generateToken
req.jwt = decoded; req.jwt = decoded
next(); next()
}); })
} }
module.exports = { module.exports = {
generateToken, generateToken,
verifyToken verifyToken
}; }

View File

@ -20,6 +20,6 @@ const knexInstance = require('knex')({
port: process.env.POSTGRES_PORT, port: process.env.POSTGRES_PORT,
database: 'Blink' database: 'Blink'
} }
}); })
module.exports = knexInstance; module.exports = knexInstance

View File

@ -17,8 +17,8 @@
* @returns true or false * @returns true or false
*/ */
function validateEmail (email) { function validateEmail (email) {
const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/; const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/
return regex.test(email); return regex.test(email)
} }
/** /**
@ -28,11 +28,11 @@ function validateEmail(email) {
* @returns true or false * @returns true or false
*/ */
function isPostgresDateFormatValid (dateString) { function isPostgresDateFormatValid (dateString) {
const regex = /^\d{4}-\d{2}-\d{2}$/; const regex = /^\d{4}-\d{2}-\d{2}$/
return regex.test(dateString); return regex.test(dateString)
} }
module.exports = { module.exports = {
validateEmail, validateEmail,
isPostgresDateFormatValid isPostgresDateFormatValid
}; }

View File

@ -1,30 +1,30 @@
// Run me with "npm test" // Run me with "npm test"
const request = require('supertest'); const request = require('supertest')
const app = require('../src/app'); const app = require('../src/app')
require('dotenv').config({ path: '../src/.env' }); require('dotenv').config({ path: '../src/.env' })
describe('Person Tests', () => { describe('Person Tests', () => {
test('Correct registration', async () => { test('Correct registration', async () => {
const response = await request(app) const response = await request(app)
.post('/api/register') .post('/api/register')
.send({ .send({
email : "johntestdoe@mail.org", email: 'johntestdoe@mail.org',
password : "password", password: 'password',
display_name : "John Doe" display_name: 'John Doe'
})
expect(response.status).toBe(200)
expect(response.body).toEqual({ activationLink: expect.any(String) })
}) })
expect(response.status).toBe(200);
expect(response.body).toEqual({ activationLink: expect.any(String) });
});
test('Incorrect registration', async () => { test('Incorrect registration', async () => {
const response = await request(app) const response = await request(app)
.post('/api/register') .post('/api/register')
.send({ .send({
email : "this is not an email", email: 'this is not an email',
password : "password", password: 'password',
display_name : "John Doe" display_name: 'John Doe'
})
expect(response.status).toBe(400)
})
}) })
expect(response.status).toBe(400);
});
});

View File

@ -1 +1 @@
const apiUrl = "http://localhost:3000/blinkapi"; const apiUrl = 'http://localhost:3000/blinkapi'