Revert "ran 'npx standard --fix'"

This reverts commit 0dc67edc9b.
This commit is contained in:
xfarrow
2024-02-22 17:39:29 +01:00
parent 0dc67edc9b
commit 2a321916ff
12 changed files with 596 additions and 564 deletions

View File

@ -2,64 +2,65 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
// 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

@ -2,116 +2,116 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config') const knex = require('../utils/knex_config');
/** /**
* Create Organization object * Create Organization object
* @param {*} name * @param {*} name
* @param {*} location * @param {*} location
* @param {*} description * @param {*} description
* @param {*} is_hiring * @param {*} is_hiring
* @returns * @returns
*/ */
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;
} }
/** /**
* Gets an Organization by its identifier * Gets an Organization by its identifier
* @param {*} id * @param {*} id
* @returns * @returns
*/ */
async function getOrganizationById (id) { 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;
} }
/** /**
* Insert an Organization and its relative Administrator * Insert an Organization and its relative Administrator
* @param {*} organization * @param {*} organization
*/ */
async function insertOrganization (organization, organizationAdministratorId) { async function insertOrganization(organization, organizationAdministratorId){
await knex.transaction(async (trx) => { await knex.transaction(async (trx) => {
// 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,
}) });
}) });
} }
/** /**
* Updates an Organization specified by the OrganizationId, if and * Updates an Organization specified by the OrganizationId, if and
* only if the specified personId is one of its Administrator * only if the specified personId is one of its Administrator
* @param {*} organization * @param {*} organization
* @param {*} organizationId * @param {*} organizationId
* @param {*} personId * @param {*} personId
* @returns true if the row was updated, false otherwise * @returns true if the row was updated, false otherwise
*/ */
async function updateOrganizationIfAdministrator (organization, organizationId, personId) { async function updateOrganizationIfAdministrator(organization, organizationId, personId){
// // const isOrganizationAdmin = await knex('OrganizationAdministrator') // // const isOrganizationAdmin = await knex('OrganizationAdministrator')
// // .where('id_person', req.jwt.person_id) // // .where('id_person', req.jwt.person_id)
// // .where('id_organization', req.params.id) // // .where('id_organization', req.params.id)
// // .select('*') // // .select('*')
// // .first(); // // .first();
// // // This introduces a Time of check Time of use weakeness // // // This introduces a Time of check Time of use weakeness
// // // which could'have been fixed by either // // // which could'have been fixed by either
// // // 1) Using "whereExists", thanks to the "it's easier to ask for // // // 1) Using "whereExists", thanks to the "it's easier to ask for
// // // forgiveness than for permission" padarigm. Or, // // // forgiveness than for permission" padarigm. Or,
// // // 2) Using a serializable transaction. // // // 2) Using a serializable transaction.
// // // // // //
// // // The undersigned chose not to follow these approaches because // // // The undersigned chose not to follow these approaches because
// // // this does not introduces any serious vulnerability. In this // // // this does not introduces any serious vulnerability. In this
// // // way it seems more readable. // // // way it seems more readable.
// // if(!isOrganizationAdmin){ // // if(!isOrganizationAdmin){
// // return res.status(403).json({error : "Forbidden"}); // // return res.status(403).json({error : "Forbidden"});
// // } // // }
// // await knex('Organization') // // await knex('Organization')
// // .where('id', req.params.id) // // .where('id', req.params.id)
// // .update({ // // .update({
// // name: req.body.name, // // name: req.body.name,
// // location: req.body.location, // // location: req.body.location,
// // description: req.body.description, // // description: req.body.description,
// // is_hiring: req.body.is_hiring // // is_hiring: req.body.is_hiring
// // }); // // });
const numberOfUpdatedRows = await knex('Organization') const numberOfUpdatedRows = await knex('Organization')
.where('id', organizationId) .where('id', organizationId)
.whereExists(function () { .whereExists(function(){
this.select('*') this.select('*')
.from('OrganizationAdministrator') .from('OrganizationAdministrator')
.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;
} }
/** /**
@ -121,17 +121,17 @@ async function updateOrganizationIfAdministrator (organization, organizationId,
* @param {*} personId PersonId of the supposedly administrator * @param {*} personId PersonId of the supposedly administrator
* @returns true if the Organization was successfully deleted, false otherwise * @returns true if the Organization was successfully deleted, false otherwise
*/ */
async function deleteOrganizationIfAdmin (organizationId, personId) { async function deleteOrganizationIfAdmin(organizationId, personId){
const numberOfDeletedRows = await knex('Organization') const numberOfDeletedRows = await knex('Organization')
.where({ id: organizationId }) .where({ id: organizationId })
.whereExists(function () { .whereExists(function(){
this.select('*') this.select('*')
.from('OrganizationAdministrator') .from('OrganizationAdministrator')
.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

@ -2,40 +2,40 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
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
* @param {*} email * @param {*} email
* @param {*} password * @param {*} password
* @param {*} display_name * @param {*} display_name
* @param {*} date_of_birth * @param {*} date_of_birth
* @param {*} available * @param {*} available
* @param {*} enabled * @param {*} enabled
* @param {*} place_of_living * @param {*} place_of_living
* @returns * @returns
*/ */
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;
} }
/** /**
@ -43,22 +43,22 @@ function person (email, password, display_name, date_of_birth, available, enable
* @param {*} email email to look the Person for * @param {*} email email to look the Person for
* @returns the Person object * @returns the Person object
*/ */
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();
} }
/** /**
* Get Person by Id * Get Person by Id
* @param {*} id - The id to look the person for * @param {*} id - The id to look the person for
* @returns * @returns
*/ */
async function getPersonById (id) { async function getPersonById(id){
return await knex('Person') return await knex('Person')
.select('*') .select('*')
.where({ id }) .where({ id: id })
.first() .first();
} }
/** /**
@ -67,27 +67,27 @@ async function getPersonById (id) {
* @param {*} person A Person object * @param {*} person A Person object
* @param {*} activationLink the activationLink identifier * @param {*} activationLink the activationLink identifier
*/ */
async function registerPerson (person, activationLink) { async function registerPerson(person, activationLink){
// We need to insert either both in the "Person" table // We need to insert either both in the "Person" table
// and in the "ActivationLink" one, or in neither // and in the "ActivationLink" one, or in neither
await knex.transaction(async (tr) => { await knex.transaction(async (tr) => {
const personIdResult = await tr('Person') const personIdResult = await tr('Person')
.insert({ .insert({
email: person.email.toLowerCase(), email: person.email.toLowerCase(),
password: person.password, password: person.password,
display_name: person.display_name, display_name: person.display_name,
date_of_birth: person.date_of_birth, date_of_birth: person.date_of_birth,
available: person.available, available: person.available,
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
}) });
}) });
} }
/** /**
@ -95,22 +95,22 @@ async function registerPerson (person, activationLink) {
* Used for log-in * Used for log-in
* @param {*} email * @param {*} email
* @param {*} password * @param {*} password
* @returns * @returns
*/ */
async function getPersonByEmailAndPassword (email, password) { async function getPersonByEmailAndPassword(email, password){
const person = await knex('Person') const person = await knex('Person')
.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
} }
/** /**
@ -118,31 +118,32 @@ async function getPersonByEmailAndPassword (email, password) {
* @param {*} person The Person to update * @param {*} person The Person to update
* @param {*} person_id The database id of the Person to update * @param {*} person_id The database id of the Person to update
*/ */
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);
} }
/** /**
* Deletes a Person specified by its database id. * Deletes a Person specified by its database id.
* @param {*} person_id * @param {*} 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.
module.exports = { module.exports = {
person, person,
getPersonByEmail, getPersonByEmail,
getPersonById, getPersonById,
getPersonByEmailAndPassword, getPersonByEmailAndPassword,
registerPerson, registerPerson,
updatePerson, updatePerson,
deletePerson deletePerson
} };

View File

@ -2,71 +2,75 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
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
* *
* Add an Administrator to an Organization. Allowed only if the * Add an Administrator to an Organization. Allowed only if the
* logged user is an Administrator themselves. * logged user is an Administrator themselves.
* *
* 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
if (!req.body.organization_id || !req.body.person_id) {
return res.status(400).json({ error: 'Invalid request' })
}
try { // Ensure that the required fields are present before proceeding
const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id) if (!req.body.organization_id || !req.body.person_id) {
// TOC/TOU return res.status(400).json({ error : "Invalid request"});
if (!isPersonAdmin) { }
return res.status(401).json({ error: 'Forbidden' })
try {
const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id);
// TOC/TOU
if(!isPersonAdmin){
return res.status(401).json({error : "Forbidden"});
}
await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id);
return res.status(200).json({success : true});
}
catch (error) {
console.error('Error while adding organization admin: ' + error);
res.status(500).json({error : "Internal server error"});
} }
await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id)
return res.status(200).json({ success: true })
} catch (error) {
console.error('Error while adding organization admin: ' + error)
res.status(500).json({ error: 'Internal server error' })
}
} }
/** /**
* DELETE Request * DELETE Request
* *
* Deletes a Person from the list of Administrators of an Organization. * Deletes a Person from the list of Administrators of an Organization.
* The logged user can only remove themselves. If no more Administrators * The logged user can only remove themselves. If no more Administrators
* are left, the Organization is removed. * are left, the Organization is removed.
* *
* 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
if (!req.body.organization_id) { // Ensure that the required fields are present before proceeding
return res.status(400).json({ error: 'Invalid request' }) if (!req.body.organization_id) {
} return res.status(400).json({ error : "Invalid request"});
}
try {
await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id) try{
return res.status(200).json({ success: true }) await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id);
} 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"});
}
} }
module.exports = { module.exports = {
addOrganizationAdmin, addOrganizationAdmin,
removeOrganizationAdmin removeOrganizationAdmin
} };

View File

@ -2,90 +2,95 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config') const knex = require('../utils/knex_config');
/** /**
* POST Request * POST Request
* *
* Creates a Post belonging to an organization * Creates a Post belonging to an organization
* *
* Required field(s): organization_id, content * Required field(s): organization_id, content
* @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
if (!req.body.organization_id || !req.body.content) {
return res.status(400).json({ error: 'Invalid request' })
}
try { // Ensure that the required fields are present before proceeding
// Check if the current user is a organization's administrator if (!req.body.organization_id || !req.body.content) {
const isOrganizationAdmin = await knex('OrganizationAdministrator') return res.status(400).json({ error : "Invalid request"});
}
try {
// Check if the current user is a organization's administrator
const isOrganizationAdmin = await knex('OrganizationAdministrator')
.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')
.insert({ .insert({
organization_id: req.body.organization_id, organization_id: req.body.organization_id,
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) { }
console.log('Error while creating Organization Post: ' + error) catch (error) {
return res.status(500).json({ error: 'Internal server error' }) console.log("Error while creating Organization Post: " + error);
return res.status(500).json({error : "Internal server error"});
}
} }
}
/** /**
* DELETE Request * DELETE Request
* *
* Deletes a Post belonging to an Organization, only if * Deletes a Post belonging to an Organization, only if
* the logged user is an administrator of that Organization. * the logged user is an administrator of that Organization.
* *
* Required field(s): none. * Required field(s): none.
*/ */
async function deleteOrganizationPost (req, res) { async function deleteOrganizationPost(req, res){
const organizationPostIdToDelete = req.params.id
try { const organizationPostIdToDelete = req.params.id;
try{
const isOrganizationAdmin = await knex('OrganizationPost') const isOrganizationAdmin = await knex('OrganizationPost')
.join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization') .join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization')
.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{
} return res.status(401).json({error : "Forbidden"});
} catch (error) { }
console.log(error) }
res.status(500).json({ error: 'Internal server error' }) catch (error) {
console.log(error);
res.status(500).json({error : "Internal server error"});
} }
} }
@ -93,6 +98,6 @@ async function deleteOrganizationPost (req, res) {
// 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.
module.exports = { module.exports = {
createOrganizationPost, createOrganizationPost,
deleteOrganizationPost deleteOrganizationPost
} };

View File

@ -2,124 +2,133 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const organization_model = require('../models/organization_model') const organization_model = require('../models/organization_model');
/** /**
* POST Request * POST Request
* *
* Creates an Organization and its Administrator. * Creates an Organization and its Administrator.
* *
* Required field(s): name * Required field(s): name
* *
* @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) { }
console.error('Error creating Organization:', error) catch (error){
res.status(500).json({ error: 'Internal server error' }) console.error('Error creating Organization:', error);
res.status(500).json({error : "Internal server error"});
} }
} }
/** /**
* PUT Request * PUT Request
* Updates an Organization's details * Updates an Organization's details
* *
* Required field(s): none. * Required field(s): none.
*/ */
async function updateOrganization (req, res) { async function updateOrganization(req, res){
const updateOrganization = {}
if (req.body.name) { const updateOrganization = {};
updateOrganization.name = req.body.name
if(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' })
} }
} catch (error) { else{
console.log(error) return res.status(404).json({error : "Organization either not found or insufficient permissions"});
return res.status(500).json({ error: 'Internal server error' }) }
}
catch (error) {
console.log(error);
return res.status(500).json({error : "Internal server error"});
} }
} }
/** /**
* DELETE Request * DELETE Request
* *
* Deletes the specified organization if the logged user is * Deletes the specified organization if the logged user is
* one of its administrator * one of its administrator
*/ */
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 })
} }
} catch (error) { else{
console.error(error) return res.status(200).json({success: true});
return res.status(500).json({ error: 'Internal server error' }) }
}
catch (error) {
console.error(error);
return res.status(500).json({error : "Internal server error"});
} }
} }
/** /**
* GET Request * GET Request
* *
* Obtains an organization by its identifier. * Obtains an organization by its identifier.
* *
* Required field(s): none. * Required field(s): none.
* *
* @returns the organization. * @returns the organization.
*/ */
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' })
} }
} catch (error) { else{
console.error('Error retrieving an organization: ' + error) return res.status(404).json({error : "Not found"});
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"});
} }
} }
@ -128,4 +137,5 @@ module.exports = {
getOrganization, getOrganization,
updateOrganization, updateOrganization,
deleteOrganization deleteOrganization
} };

View File

@ -2,98 +2,102 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
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
* *
* Registers a Person * Registers a Person
* *
* Required field(s): name, email (valid ISO standard), password * Required field(s): name, email (valid ISO standard), password
* *
* @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,
await hashPasswordPromise, await hashPasswordPromise,
req.body.display_name, req.body.display_name,
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 }) return res.status(200).json({ activationLink: activationLink });
} catch (error) { }
console.error('Error registering person:', error) catch (error){
res.status(500).json({ error: 'Internal server error' }) console.error('Error registering person:', error);
res.status(500).json({error : "Internal server error"});
} }
} }
/** /**
* POST Request * POST Request
* *
* Creates a token if the specified email * Creates a token if the specified email
* and password are valid. * and password are valid.
* *
* Required field(s): email, password * Required field(s): email, password
* *
* @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 }) res.status(200).json({token: token });
} else {
res.status(401).json({ error: 'Unauthorized' })
} }
} catch (error) { else{
console.error('Error logging in: ', error) res.status(401).json({error : "Unauthorized"});
res.status(500).json({ error: 'Internal server error' }) }
} catch(error){
console.error('Error logging in: ', error);
res.status(500).json({error : "Internal server error"});
} }
} }
@ -101,131 +105,138 @@ async function login (req, res) {
* Obtain a Person's details if the * Obtain a Person's details if the
* Person to retrieve is either myself or an * Person to retrieve is either myself or an
* enabled Person. * enabled Person.
* *
* Required field(s): none * Required field(s): none
* *
* @returns The Person * @returns The Person
*/ */
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) { }
console.log('Error while getting person: ' + error) catch (error) {
return res.status(500).json({ error: 'Internal server error' }) console.log("Error while getting person: " + error);
return res.status(500).json({error : "Internal server error"});
} }
} }
/** /**
* *
* GET Request * GET Request
* *
* Get myself, from the JWT token * Get myself, from the JWT token
* *
* @returns Person's details * @returns Person's details
*/ */
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) { }
console.log('Error while getting myself: ' + error) catch (error){
return res.status(500).json({ error: 'Internal server error' }) console.log("Error while getting myself: " + error);
return res.status(500).json({error : "Internal server error"});
} }
} }
/** /**
* PUT request * PUT request
* *
* Updates a Person's details. If some details are * Updates a Person's details. If some details are
* not present, they shall be ignored. * not present, they shall be ignored.
* *
* Required field(s): none. Both old_password and * Required field(s): none. Both old_password and
* new_password if updating the password. * new_password if updating the password.
* *
*/ */
async function updatePerson (req, res) { async function updatePerson(req, res){
if (req.jwt.person_id != req.params.id) {
return res.status(403).json({ error: 'Forbidden' }) if (req.jwt.person_id != req.params.id){
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 { }
return res.status(400).json({ error: 'Date of birth format not valid. Please specify a YYYY-MM-DD date' }) else{
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
if (req.body.old_password && req.body.new_password) { if(req.body.old_password && req.body.new_password){
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 { }
return res.status(401).json({ error: 'Password verification failed' }) else{
} 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) { }
console.log('Error while updating a Person: ' + error) catch (error) {
return res.status(500).json({ error: 'Internal server error' }) console.log("Error while updating a Person: " + error);
return res.status(500).json({ error : "Internal server error"});
} }
} }
/** /**
* GET Request * GET Request
* *
* Deletes a Person. An user can only delete * Deletes a Person. An user can only delete
* themselves. * themselves.
* *
* Required field(s): none * Required field(s): none
* *
*/ */
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) { }
console.log('Error deleting a Person: ' + error) catch (error) {
return res.status(500).json({ error: 'Internal server error' }) console.log("Error deleting a Person: " + error);
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -233,10 +244,10 @@ async function deletePerson (req, res) {
// 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.
module.exports = { module.exports = {
registerPerson, registerPerson,
login, login,
getPerson, getPerson,
getMyself, getMyself,
updatePerson, updatePerson,
deletePerson deletePerson
} };

View File

@ -2,50 +2,50 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
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, {
expiresIn: '8h'
});
return token;
} }
const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, { // Middlware
expiresIn: '8h' function verifyToken(req, res, next) {
}) const token = req.headers.authorization;
return token
} if (!token) {
return res.status(401).send({error : 'No token provided'});
// Middlware
function verifyToken (req, res, next) {
const token = req.headers.authorization
if (!token) {
return res.status(401).send({ error: 'No token provided' })
}
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(401).send({ error: 'Failed to authenticate token' })
} }
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(401).send({error : 'Failed to authenticate token'});
}
// If the token is valid, store the decoded data in the request object
// req.jwt will contain the payload created in generateToken
req.jwt = decoded;
next();
});
}
// If the token is valid, store the decoded data in the request object module.exports = {
// req.jwt will contain the payload created in generateToken generateToken,
req.jwt = decoded verifyToken
next() };
})
}
module.exports = {
generateToken,
verifyToken
}

View File

@ -2,24 +2,24 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knexInstance = require('knex')({ const knexInstance = require('knex')({
client: 'pg', client: 'pg',
connection: { connection: {
host: process.env.POSTGRES_SERVER, host: process.env.POSTGRES_SERVER,
user: process.env.POSTGRES_USERNAME, user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD, password: process.env.POSTGRES_PASSWORD,
port: process.env.POSTGRES_PORT, port: process.env.POSTGRES_PORT,
database: 'Blink' database: 'Blink'
} }
}) });
module.exports = knexInstance module.exports = knexInstance;

View File

@ -2,37 +2,37 @@
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
/** /**
* Checks whether an e-mail is in a valid format * Checks whether an e-mail is in a valid format
* @param {*} email email to validate * @param {*} email email to validate
* @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);
} }
/** /**
* Checks whether a date is in a correct Postgres * Checks whether a date is in a correct Postgres
* format (YYYY-MM-DD) * format (YYYY-MM-DD)
* @param {*} dateString the date to validate * @param {*} dateString the date to validate
* @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.status).toBe(200);
expect(response.body).toEqual({ activationLink: expect.any(String) }) 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";