Skip to content
Merged
Show file tree
Hide file tree
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
239 changes: 239 additions & 0 deletions API/Backend/Accounts/routes/accounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/***********************************************************
* JavaScript syntax format: ES5/ES6 - ECMAScript 2015
* Loading all required dependencies, libraries and packages
**********************************************************/
const express = require("express");
const router = express.Router();
const crypto = require("crypto");

const logger = require("../../../logger");
const userModel = require("../../Users/models/user");
const User = userModel.User;

router.get("/entries", function (req, res) {
User.findAll({
attributes: [
"id",
"username",
"email",
"permission",
"createdAt",
"updatedAt",
],
order: [["id", "ASC"]],
})
.then((users) => {
res.send({
status: "success",
body: {
entries: users,
},
});
})
.catch((err) => {
logger("error", "Failed to get user entries.", req.originalUrl, req, err);
res.send({
status: "failure",
message: `Failed to get user entries.`,
});
});
});

router.delete("/remove/:id", function (req, res, next) {
const id = parseInt(req.params.id);

if (isNaN(id)) {
logger(
"error",
`Failed to delete user. User Id is null.`,
"accounts",
null
);
res.send({
status: "failure",
message: `Failed to delete user. User Id is null.`,
});
return null;
}
if (id === 1) {
logger(
"error",
`Cannot delete the original Administrator account.`,
"accounts",
null
);
res.send({
status: "failure",
message: `Cannot delete the original Administrator account.`,
});
return null;
}

User.destroy({ where: { id: id } })
.then(() => {
logger("info", `Successfully deleted user with id: '${id}'.`);
res.send({
status: "success",
message: `Successfully deleted user with id: '${id}'.`,
body: {
deleted_id: id,
},
});
})
.catch((err) => {
logger(
"error",
`Failed to delete user with id: '${id}'.`,
"accounts",
null,
err
);
res.send({
status: "failure",
message: `Failed to delete user with id: '${id}'.`,
});
return null;
});
return null;
});

router.post("/update", function (req, res, next) {
let id = null;
if (req.body.hasOwnProperty("id") && req.body.id != null) {
id = parseInt(req.body.id);
}

if (isNaN(id) || id == null) {
logger(
"error",
`Failed to update user. User Id is null.`,
"accounts",
null
);
res.send({
status: "failure",
message: `Failed to update user. User Id is null.`,
});
return null;
}

//Form update object
let toUpdateTo = {};
if (req.body.hasOwnProperty("email") && req.body.email != null) {
toUpdateTo.email = req.body.email;
}
if (
req.body.hasOwnProperty("permission") &&
req.body.permission != null &&
(req.body.permission === "111" || req.body.permission === "001")
) {
toUpdateTo.permission = req.body.permission;
}
// Don't allow changing the main admin account's permissions
if (id === 1) {
delete toUpdateTo.permission;
}

let updateObj = {
where: {
id: id,
},
};

User.update(toUpdateTo, updateObj)
.then(() => {
res.send({
status: "success",
message: `Successfully updated user with id: '${id}'.`,
body: {
updated_id: id,
},
});
return null;
})
.catch((err) => {
logger(
"error",
`Failed to update user with id: '${id}'.`,
req.originalUrl,
req,
err
);
res.send({
status: "failure",
message: `Failed updated user with id: '${id}'.`,
body: {},
});
});
});

router.post("/generateResetPasswordLink", function (req, res, next) {
let id = null;
if (req.body.hasOwnProperty("id") && req.body.id != null) {
id = parseInt(req.body.id);
}

if (isNaN(id) || id == null) {
logger(
"error",
`Failed to generate a password reset link for user. User Id is null.`,
"accounts",
null
);
res.send({
status: "failure",
message: `Failed to generate a password reset link for user. User Id is null.`,
});
return null;
}

let expires = 3600000;
if (req.body.hasOwnProperty("expires") && req.body.expires != null) {
expires = parseInt(req.body.expires);
}

if (isNaN(expires) || expires == null) {
expires = 3600000;
}

//Form update object
let toUpdateTo = {
reset_token: crypto.randomBytes(32).toString("hex"),
reset_token_expiration: Date.now() + expires,
};

let updateObj = {
where: {
id: id,
},
};

User.update(toUpdateTo, updateObj)
.then(() => {
res.send({
status: "success",
message: `Successfully generated a password reset token for user with id: '${id}'.`,
body: {
resetToken: toUpdateTo.reset_token,
resetTokenExpiration: toUpdateTo.reset_token_expiration,
},
});
return null;
})
.catch((err) => {
logger(
"error",
`Failed to generate a password reset token for user with id: '${id}'.`,
req.originalUrl,
req,
err
);
res.send({
status: "failure",
message: `Failed to generate a password reset token for user with id: '${id}'.`,
body: {},
});
});
});

module.exports = router;
21 changes: 21 additions & 0 deletions API/Backend/Accounts/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const router = require("./routes/accounts");

let setup = {
//Once the app initializes
onceInit: (s) => {
s.app.use(
s.ROOT_PATH + "/api/accounts",
s.checkHeadersCodeInjection,
s.ensureAdmin(),
s.checkHeadersCodeInjection,
s.setContentType,
router
);
},
//Once the server starts
onceStarted: (s) => {},
//Once all tables sync
onceSynced: (s) => {},
};

module.exports = setup;
2 changes: 1 addition & 1 deletion API/Backend/Config/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let setup = {
s.ensureGroup(s.permissions.users),
s.ensureAdmin(true),
(req, res) => {
const user = process.env.AUTH === "csso" ? req.user : null;
const user = process.env.AUTH === "csso" ? req.user : req.user || "";
res.render("../configure/build/index.pug", {
user: user,
AUTH: process.env.AUTH,
Expand Down
62 changes: 60 additions & 2 deletions API/Backend/Users/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ var User = sequelize.define(
User.findOne({ where: { email: value } })
.then(function (user) {
// reject if a different user wants to use the same email
if (user && self.id !== user.id) {
return next("User exists!");
if (value != null && value != "" && user && self.id !== user.id) {
return next("User email already exists!");
}
return next();
})
Expand All @@ -50,13 +50,25 @@ var User = sequelize.define(
type: Sequelize.DataTypes.STRING(2048),
allowNull: true,
},
reset_token: {
type: Sequelize.DataTypes.STRING(2048),
allowNull: true,
},
reset_token_expiration: {
type: Sequelize.DataTypes.BIGINT,
allowNull: true,
},
},
{
hooks: {
beforeCreate: (user) => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
},
beforeUpdate: (user) => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
},
},
},
{
Expand All @@ -69,5 +81,51 @@ User.prototype.validPassword = function (password, user) {
return bcrypt.compareSync(password, user.password);
};

// Adds to the table, never removes
const up = async () => {
// resetToken column
await sequelize
.query(
`ALTER TABLE users ADD COLUMN IF NOT EXISTS reset_token varchar(2048) NULL;`
)
.then(() => {
return null;
})
.catch((err) => {
logger(
"error",
`Failed to add users.reset_token column. DB tables may be out of sync!`,
"user",
null,
err
);
return null;
});

// resetTokenExpiration column
await sequelize
.query(
`ALTER TABLE users ADD COLUMN IF NOT EXISTS reset_token_expiration BIGINT NULL;`
)
.then(() => {
return null;
})
.catch((err) => {
logger(
"error",
`Failed to add users.reset_token_expiration column. DB tables may be out of sync!`,
"user",
null,
err
);
return null;
});
};

// export User model for use in other files.
module.exports = User;

module.exports = {
User: User,
up,
};
Loading