Create person_model.js

This commit is contained in:
xfarrow 2024-02-21 10:22:17 +01:00
parent bcf54f23be
commit cfdac4872e
14 changed files with 209 additions and 117 deletions

View File

@ -0,0 +1,144 @@
/*
This code is part of Blink
licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
IN THE SOFTWARE.
*/
const knex = require('../utils/knex_config');
const bcrypt = require('bcrypt');
/**
* Creates Person object by the specified fields
* @param {*} email
* @param {*} password
* @param {*} display_name
* @param {*} date_of_birth
* @param {*} available
* @param {*} enabled
* @param {*} place_of_living
* @returns
*/
function createPerson(email, password, display_name, date_of_birth, available, enabled, place_of_living) {
const person = {
email: email,
password: password,
display_name: display_name,
date_of_birth: date_of_birth,
available: available,
enabled: enabled,
place_of_living: place_of_living
};
return person;
}
/**
* Returns the Person specified by their e-mail
* @param {*} email email to look the Person for
* @returns the Person object
*/
async function getPersonByEmail(email){
return await knex('Person')
.where('email', email)
.first();
}
async function getPersonById(id){
return await knex('Person')
.select('*')
.where({ id: id })
.first();
}
/**
* Registers a Person by inserting in the database, in a transaction,
* both in the "Person" and in the "ActivationLink" tables.
* @param {*} person A Person object
* @param {*} activationLink the activationLink identifier
*/
async function registerPerson(person, activationLink){
// We need to insert either both in the "Person" table
// and in the "ActivationLink" one, or in neither
await knex.transaction(async (tr) => {
const personIdResult = await tr('Person')
.insert({
email: person.email,
password: person.password,
display_name: person.display_name,
date_of_birth: person.date_of_birth,
available: person.available,
enabled: person.enabled,
place_of_living: person.place_of_living
})
.returning("id");
await tr('ActivationLink')
.insert({
person_id: personIdResult[0].id,
identifier: activationLink
});
});
}
/**
* Gets a Person by specifying email and password.
* Used for log-in
* @param {*} email
* @param {*} password
* @returns
*/
async function getPersonByEmailAndPassword(email, password){
const person = await knex('Person')
.where('email', email)
.where('enabled', true)
.select('*')
.first();
if(person){
const passwordMatches = await bcrypt.compare(password, person.password);
if (passwordMatches) {
return person;
}
}
return null;
}
/**
* Update a Person
* @param {*} person The Person to update
* @param {*} person_id The database id of the Person to update
*/
async function updatePerson(person, person_id){
await knex('Person')
.where('id', person_id)
.update(person);
}
/**
* Deletes a Person specified by its database id.
* @param {*} person_id
*/
async function deletePerson(person_id){
await knex('Person')
.where({id : person_id})
.del();
}
// Exporting a function
// means making a JavaScript function defined in one
// module available for use in another module.
module.exports = {
createPerson,
getPersonByEmail,
getPersonById,
getPersonByEmailAndPassword,
registerPerson,
updatePerson,
deletePerson
};

View File

@ -16,6 +16,7 @@ const knex = require('../utils/knex_config');
const jwt_utils = require('../utils/jwt_utils');
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const person_model = require('../models/person_model');
/**
* POST Request
@ -28,64 +29,44 @@ const crypto = require('crypto');
*/
async function registerPerson(req, res){
if (process.env.ALLOW_USER_REGISTRATION === 'false'){
return res.status(403).json({error : "Users cannot register on this server"});
}
// Ensure that the required fields are present before proceeding
if (!req.body.display_name || !req.body.email || !req.body.password) {
return res.status(400).json({ error : "Some or all required fields are missing"});
}
if(!validator.validateEmail(req.body.email)){
return res.status(400).json({ error : "The email is not in a valid format"});
}
// Generate activation link token
const activationLink = crypto.randomBytes(16).toString('hex');
// Hash provided password
const hashPasswordPromise = bcrypt.hash(req.body.password, 10);
try{
// Check whether e-mail exists already (enforced by database constraints)
const existingUser = await knex('Person')
.where('email', req.body.email)
.first();
if(existingUser){
return res.status(409).json({ error: "E-mail already in use" });
}
// We need to insert either both in the "Person" table
// and in the "ActivationLink" one, or in neither
await knex.transaction(async (tr) => {
const personIdResult = await tr('Person')
.insert({
email: req.body.email,
password: await hashPasswordPromise,
display_name: req.body.display_name,
date_of_birth: req.body.date_of_birth,
available: req.body.available,
enabled: true,
place_of_living: req.body.place_of_living
})
.returning("id");
await tr('ActivationLink')
.insert({
person_id: personIdResult[0].id,
identifier: activationLink
});
});
return res.status(200).json({ activationLink: activationLink });
}
catch (error){
console.error('Error registering person:', error);
res.status(500).json({error : "Internal server error"});
// Does this server allow users to register?
if (process.env.ALLOW_USER_REGISTRATION === 'false'){
return res.status(403).json({error : "Users cannot register on this server"});
}
// Ensure that the required fields are present before proceeding
if (!req.body.display_name || !req.body.email || !req.body.password) {
return res.status(400).json({ error : "Some or all required fields are missing"});
}
if(!validator.validateEmail(req.body.email)){
return res.status(400).json({ error : "The email is not in a valid format"});
}
// Generate activation link token
const activationLink = crypto.randomBytes(16).toString('hex');
// Hash provided password
const hashPasswordPromise = bcrypt.hash(req.body.password, 10);
try{
// Check whether e-mail exists already (enforced by database constraints)
const existingUser = await person_model.getPersonByEmail(req.body.email);
if(existingUser){
return res.status(409).json({ error: "E-mail already in use" });
}
const personToInsert = person_model.createPerson(
req.body.email,
await hashPasswordPromise,
req.body.display_name,
req.body.date_of_birth,
req.body.available,
true,
req.body.place_of_living);
await person_model.registerPerson(personToInsert, activationLink);
return res.status(200).json({ activationLink: activationLink });
}
catch (error){
console.error('Error registering person:', error);
res.status(500).json({error : "Internal server error"});
}
}
/**
@ -105,14 +86,18 @@ async function login(req, res){
return res.status(400).json({error : "Invalid request"});
}
const person = await checkUserCredentials(req.body.email, req.body.password);
if (person){
const token = jwt_utils.generateToken(person.id);
res.status(200).json({token: token });
}
else{
res.status(401).json({error : "Unauthorized"});
try{
const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password);
if (person){
const token = jwt_utils.generateToken(person.id);
res.status(200).json({token: token });
}
else{
res.status(401).json({error : "Unauthorized"});
}
} catch(error){
console.error('Error logging in: ', error);
res.status(500).json({error : "Internal server error"});
}
}
@ -127,16 +112,12 @@ async function login(req, res){
*/
async function getPerson(req, res){
try {
const user = await knex('Person')
.select('*')
.where({ id: req.params.id })
.first();
if(user){
const person = await person_model.getPersonById(req.params.id);
if(person){
// I am retrieving either myself or an enabled user
if(user.id == req.jwt.person_id || user.enabled){
delete user['password']; // remove password field for security reasons
return res.status(200).send(user);
if(person.id == req.jwt.person_id || person.enabled){
delete person['password']; // remove password field for security reasons
return res.status(200).send(person);
}
}
return res.status(404).json({error: "Not found"});
@ -157,18 +138,12 @@ async function getPerson(req, res){
*/
async function getMyself(req, res){
try{
const person = await knex('Person')
.select('*')
.where({ id: req.jwt.person_id })
.first();
console.log(req.jwt.person_id);
if(person){
delete person['password'];
return res.status(200).send(person);
}
return res.status(404).json({error: "Not found"});
const person = await person_model.getPersonById(req.jwt.person_id);
if(person){
delete person['password'];
return res.status(200).send(person);
}
return res.status(404).json({error: "Not found"});
}
catch (error){
console.log("Error while getting myself: " + error);
@ -214,7 +189,7 @@ async function updatePerson(req, res){
if(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(req.body.old_password && req.body.new_password){
const user = await knex('Person')
@ -235,9 +210,7 @@ async function updatePerson(req, res){
}
try {
await knex('Person')
.where('id', req.params.id)
.update(updatePerson);
await person_model.updatePerson(updatePerson, req.params.id);
return res.status(200).json({ success : "true"});
}
catch (error) {
@ -256,13 +229,10 @@ async function updatePerson(req, res){
*
*/
async function deletePerson(req, res) {
// TODO: Delete Organization if this user was its only administrator
try {
await knex('Person')
.where({id : req.jwt.person_id})
.del();
await person_model.deletePerson(req.jwt.person_id);
return res.status(200).json({success: true});
// TODO: Delete Organization if this user was its only administrator
}
catch (error) {
console.log("Error deleting a Person: " + error);
@ -270,28 +240,6 @@ async function deletePerson(req, res) {
}
}
async function checkUserCredentials(email, password){
try {
const user = await knex('Person')
.where('email', email)
.where('enabled', true)
.select('*')
.first();
if(user){
const passwordMatches = await bcrypt.compare(password, user.password);
if (passwordMatches) {
return user;
}
}
return null;
}
catch (error) {
console.log(error);
return null;
}
}
// Exporting a function
// means making a JavaScript function defined in one
// module available for use in another module.

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB