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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
.DS_Store
node_modules
/dist

package-lock.json
server/.nyc_output/*.json
server/.nyc_output/processinfo/*.json

# local env files
.env.local
Expand Down
1 change: 1 addition & 0 deletions server/.nyc_output/processinfo/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"processes":{"ecd4285b-d9dc-4104-b850-fcf81a763c89":{"parent":null,"children":[]}},"files":{"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\app.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\database\\index.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\config\\db.config.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\models\\user.model.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\routes\\auth.routes.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\controllers\\auth.controller.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\business\\user.business.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"],"C:\\Users\\Jarrod\\Documents\\AAF\\CSSD\\server\\datalayer\\mongo.js":["ecd4285b-d9dc-4104-b850-fcf81a763c89"]},"externalIds":{}}
14 changes: 14 additions & 0 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@ const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const cors = require("cors");
const cookieSession = require("cookie-session");
const cookieParser = require("cookie-parser");

require("./database");
require("dotenv").config();

var app = express();

app.use(
cookieSession({
name: "highwayTracker-token",
secret: process.env.TOKEN_SECRET,
httpOnly: true,
keys: [process.env.TOKEN_SECRET],
})
);

app.use(cors({ origin: "http://localhost:8080", credentials: true }));

/**
* Router setup
*/
var authRouter = require("./routes/auth.routes");

/**
* View Engine setup
Expand All @@ -24,7 +36,9 @@ app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, "public")));
app.use(cookieParser());

// Configuring the main routes
app.use("/auth", authRouter);

module.exports = app;
118 changes: 118 additions & 0 deletions server/business/user.business.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
const DataLayer = require("../datalayer/mongo");
const model = require("../database").getModel("user");
const httpError = require("http-errors");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

class UserBusiness {
constructor() {
// Create an instance of the data layer.
this.dataLayer = new DataLayer(model);
}

/**
* Login a user.
*/
async login(email, password) {
return this.findUserByEmail(email)
.then((user) => {
const passwordIsValid = bcrypt.compareSync(
password,
user.password
);
// Invalid password, return 401
if (!passwordIsValid) {
throw httpError(
401,
"Your email or password is incorrect."
);
}
// Create token and store in the session cookie
const token = jwt.sign(
{
id: user._id,
type: user.type,
email: user.email,
username: user.username,
},
process.env.TOKEN_SECRET,
{
expiresIn: 3600, // 1 hour
}
);
return {
token: token,
username: user.username,
type: user.type,
id: user._id,
};
})
.catch(() => {
throw httpError(400, "Your email or password is incorrect.");
});
}

/**
* Register a user.
*/
async register(user) {
return this.createUser({
username: user.username,
email: user.email,
password: user.password,
type: "Driver",
}).catch((error) => {
throw httpError(400, error.message);
});
}

/**
* Create a user and save it to the User collection.
*/
async createUser(userToCreate) {
if (!isUserDataValid(userToCreate)) {
throw httpError(400, "User data is invalid.");
}
const user = {
username: userToCreate.username,
email: userToCreate.email,
type: userToCreate.type,
password: bcrypt.hashSync(userToCreate.password, 8),
};

return this.dataLayer.create(user).catch((error) => {
if (error.message.includes("username"))
throw httpError(400, "Username is already in use.");
if (error.message.includes("email"))
throw httpError(400, "Email is already in use.");
throw httpError(404, error.message);
});
}

/**
* Find a user by email
*/
async findUserByEmail(email) {
return this.dataLayer
.findByProperty({ email: email })
.then((users) => {
// Email is unique so only 1 can be returned.
return users[0];
})
.catch((error) => {
throw httpError(404, error.message);
});
}
}
module.exports = UserBusiness;

/**
* Validates the data in a User.
*/
function isUserDataValid(user) {
if (!user || !user.username || !user.email || !user.password) {
return false;
} else {
return true;
}
}
7 changes: 6 additions & 1 deletion server/config/db.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module.exports = {
url: "mongodb://localhost:27017/highwaytrackerdb",
dev: {
url: "mongodb://localhost:27017/highwaytrackerdb",
},
test: {
url: "mongodb://localhost:27017/highwaytrackerdb_testing",
},
};
52 changes: 52 additions & 0 deletions server/controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const UserBusiness = require("../business/user.business");
const userBusiness = new UserBusiness();

/**
* Login the user
*/
exports.login = async (req, res) => {
userBusiness
.login(req.body.email, req.body.password)
.then((data) => {
req.session.token = data.token;
req.session.username = data.username;
req.session.role = data.role;
req.session.id = data.id;

res.status(200).send({
message: "Successfully logged in.",
username: data.username,
role: data.role,
id: data.id,
});
})
.catch((error) => {
res.status(error.status).send({ message: error.message });
});
};

/**
* Register the user
*/
exports.register = (req, res) => {
userBusiness
.register(req.body)
.then(() => {
res.status(201).send({
message: "User was successfully created.",
});
})
.catch((error) => {
res.status(error.status).send({ message: error.message });
});
};

/**
* Logs the user out
*/
exports.logout = (req, res) => {
req.session = null;
res.status(200).send({
message: "User was successfully logged out.",
});
};
14 changes: 8 additions & 6 deletions server/database/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// Get database config
const environment = process.env.NODE_ENV;
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
const dbConfig = require("../config/db.config.js");
const journey = require("../models/journey")(mongoose)
const bill = require("../models/bill")(mongoose)
const location = require("../models/location")(mongoose)
const environment = process.env.NODE_ENV;
const dbConfig = require("../config/db.config.js")[environment];

const journey = require("../models/journey")(mongoose);
const bill = require("../models/bill")(mongoose);
const location = require("../models/location")(mongoose);
const user = require("../models/user.model.js")(mongoose);

// Create mongoose and read in config
const db = {journey: journey, bill: bill, location: location};
const db = { journey: journey, bill: bill, location: location, user: user };
db.mongoose = mongoose;
db.url = dbConfig.url;

Expand Down
39 changes: 39 additions & 0 deletions server/database/seed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const mongoose = require("mongoose");
mongoose.users = require("../models/user.model")(mongoose);
const bcrypt = require("bcryptjs");

mongoose
.connect("mongodb://localhost:27017/highwaytrackerdb_testing", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connection to database successful.");
})
.catch(() => {
console.log("Connection to database unsuccessful.");
});

const users = [
{
username: "test_username",
email: "test@email.com",
password: bcrypt.hashSync("admin", 8),
type: "Driver",
},
];
const seedDB = async () => {
await mongoose.users.deleteMany();
await mongoose.users.insertMany(users);
};

seedDB()
.then(() => {
console.log("Successfully seeded database.");
})
.catch((error) => {
console.log("An error occurred while seeding databases: ", error);
})
.finally(() => {
mongoose.connection.close();
});
22 changes: 22 additions & 0 deletions server/datalayer/mongo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class DataLayer {
constructor(model) {
// Set the collections model to use.
this.model = model;
}

/**
* Create and save the record to the database.
*/
async create(recordToCreate) {
return this.model.create(recordToCreate);
}

/**
* Find a record by property in the database.
*/
async findByProperty(propertyToFind) {
return this.model.find(propertyToFind);
}
}

module.exports = DataLayer;
42 changes: 42 additions & 0 deletions server/middleware/auth/authJwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const jwt = require("jsonwebtoken");

// Check if token is valid
checkJwtToken = (req, res, next) => {
if (!req.session || !req.session.token) {
return res.status(401).send({
message: "Unauthorized: No token provided.",
});
}
const token = req.session.token;

jwt.verify(token, process.env.TOKEN_SECRET, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized: Invalid token.",
});
}

req.userId = decoded.id;
req.username = decoded.username;
req.email = decoded.email;
req.type = decoded.type;
return next();
});
};

isOperator = (req, res, next) => {
const type = req.type;

if (type != "Toll Operator") {
return res.status(403).send({
message:
"Unauthorized: You not do have permission to view this page.",
});
}
return next();
};

module.exports = {
checkJwtToken,
isOperator,
};
31 changes: 31 additions & 0 deletions server/models/user.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Model for the User
module.exports = (mongoose) => {
var UserSchema = mongoose.Schema({
username: {
type: String,
required: [true, "You must supply the user's username."],
minlength: [5, "Your username must be at least 5 letters."],
unique: true,
},
email: {
type: String,
required: [true, "You must supply the user's email."],
unique: true,
},
password: {
type: String,
required: [true, "You must supply the user's password"],
minlength: [5, "Your password must be at least 8 letters."],
},
type: {
type: String,
required: [true, "You must supply the user's role."],
enum: {
values: ["Driver", "Toll Operator"],
message: "Type is not valid. Must be 'Driver'.",
},
},
});

return mongoose.model("user", UserSchema);
};
Loading