Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 80 additions & 92 deletions app/data/allocations-dao.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,102 @@
const UserDAO = require("./user-dao").UserDAO;

/* The AllocationsDAO must be constructed with a connected database object */
const AllocationsDAO = function(db){
const AllocationsDAO = function (db) {

"use strict";
"use strict";

/* If this constructor is called without the "new" operator, "this" points
* to the global object. Log a warning and call it correctly. */
if (false === (this instanceof AllocationsDAO)) {
console.log("Warning: AllocationsDAO constructor called without 'new' operator");
return new AllocationsDAO(db);
}
/* If this constructor is called without the "new" operator, "this" points
* to the global object. Log a warning and call it correctly. */
if (false === (this instanceof AllocationsDAO)) {
console.log("Warning: AllocationsDAO constructor called without 'new' operator");
return new AllocationsDAO(db);
}

const allocationsCol = db.collection("allocations");
const userDAO = new UserDAO(db);
const allocationsCol = db.collection("allocations");
const userDAO = new UserDAO(db);

this.update = (userId, stocks, funds, bonds, callback) => {
const parsedUserId = parseInt(userId);
this.update = (userId, stocks, funds, bonds, callback) => {
const parsedUserId = parseInt(userId, 10);

// Create allocations document
const allocations = {
userId: userId,
stocks: stocks,
funds: funds,
bonds: bonds
};
// Create allocations document
const allocations = {
userId: parsedUserId,
stocks: stocks,
funds: funds,
bonds: bonds
};

allocationsCol.update({
userId: parsedUserId
}, allocations, {
upsert: true
}, err => {
// Use $set + upsert to avoid replacing the whole document accidentally
allocationsCol.updateOne(
{ userId: parsedUserId },
{ $set: allocations },
{ upsert: true },
(err) => {
if (err) return callback(err, null);

if (!err) {
console.log("Updated allocations");

console.log("Updated allocations");
userDAO.getUserById(parsedUserId, (err, user) => {
if (err) return callback(err, null);

userDAO.getUserById(userId, (err, user) => {
// add user details
allocations.userId = parsedUserId;
allocations.userName = user.userName;
allocations.firstName = user.firstName;
allocations.lastName = user.lastName;

if (err) return callback(err, null);
return callback(null, allocations);
});
}
);
};

this.getByUserIdAndThreshold = (userId, threshold, callback) => {
const parsedUserId = parseInt(userId, 10);

// build safe criteria (avoid $where / JS execution)
let criteria = { userId: parsedUserId };

if (threshold !== undefined && threshold !== null && threshold !== '') {
const parsedThreshold = parseInt(threshold, 10);
if (Number.isNaN(parsedThreshold) || parsedThreshold < 0) {
return callback(`The user supplied threshold: ${threshold} was not valid.`, null);
}
criteria.stocks = { $gt: parsedThreshold };
}

// add user details
allocations.userId = userId;
allocations.userName = user.userName;
allocations.firstName = user.firstName;
allocations.lastName = user.lastName;
allocationsCol.find(criteria).toArray((err, allocations) => {
if (err) return callback(err, null);
if (!allocations.length) return callback("ERROR: No allocations found for the user", null);

return callback(null, allocations);
});
}
let doneCounter = 0;
const userAllocations = [];
let called = false; // guard to avoid multiple callbacks

allocations.forEach(alloc => {
userDAO.getUserById(alloc.userId, (err, user) => {
if (called) return;
if (err) {
called = true;
return callback(err, null);
});
};
}

alloc.userName = user.userName;
alloc.firstName = user.firstName;
alloc.lastName = user.lastName;

this.getByUserIdAndThreshold = (userId, threshold, callback) => {
const parsedUserId = parseInt(userId);

const searchCriteria = () => {

if (threshold) {
/*
// Fix for A1 - 2 NoSQL Injection - escape the threshold parameter properly
// Fix this NoSQL Injection which doesn't sanitze the input parameter 'threshold' and allows attackers
// to inject arbitrary javascript code into the NoSQL query:
// 1. 0';while(true){}'
// 2. 1'; return 1 == '1
// Also implement fix in allocations.html for UX.
const parsedThreshold = parseInt(threshold, 10);

if (parsedThreshold >= 0 && parsedThreshold <= 99) {
return {$where: `this.userId == ${parsedUserId} && this.stocks > ${parsedThreshold}`};
}
throw `The user supplied threshold: ${parsedThreshold} was not valid.`;
*/
return {
$where: `this.userId == ${parsedUserId} && this.stocks > '${threshold}'`
};
}
return {
userId: parsedUserId
};
};

allocationsCol.find(searchCriteria()).toArray((err, allocations) => {
if (err) return callback(err, null);
if (!allocations.length) return callback("ERROR: No allocations found for the user", null);

let doneCounter = 0;
const userAllocations = [];

allocations.forEach( alloc => {
userDAO.getUserById(alloc.userId, (err, user) => {
if (err) return callback(err, null);

alloc.userName = user.userName;
alloc.firstName = user.firstName;
alloc.lastName = user.lastName;

doneCounter += 1;
userAllocations.push(alloc);

if (doneCounter === allocations.length) {
callback(null, userAllocations);
}
});
});
doneCounter += 1;
userAllocations.push(alloc);

if (doneCounter === allocations.length) {
called = true;
callback(null, userAllocations);
}
});
};
});
});
};

};

module.exports.AllocationsDAO = AllocationsDAO;
module.exports.AllocationsDAO = AllocationsDAO;