Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code #132

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Code #132

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
105 changes: 84 additions & 21 deletions api/auth/auth-middleware.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,85 @@
const { JWT_SECRET } = require("../secrets"); // use this secret!
const jwt = require("jsonwebtoken");
const { JWT_SECRET } = require("../secrets");
const Users = require("./../users/users-model");

const restricted = (req, res, next) => {
/*
const token = req.headers.authorization;

if (!token) {
next({ status: 401, message: "Token required" });
} else {
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) {
delete req.decodedJwt;
next({
status: 401,
message: "Token invalid",
});
} else {
req.decodedJwt = decoded;
next();
}
});
}
};

const only = (role_name) => (req, res, next) => {
if (req.decodedJwt.role_name !== role_name) {
next({
status: 403,
message: "This is not for you",
});
} else {
next();
}
};

const checkUsernameExists = async (req, res, next) => {
try {
const { username } = req.body;
const user = await Users.findBy({ username });
if (user.length === 0) {
next({ status: 401, message: "Invalid credentials" });
} else {
req.userFromDb = user[0];
next();
}
} catch (err) {
next(err);
}
};

const validateRoleName = (req, res, next) => {
if (req.body.role_name === undefined) {
req.body.role_name = "student";
next();
} else {
const trimmedRole = req.body.role_name.trim();
if (!trimmedRole) {
req.body.role_name = "student";
next();
} else if (trimmedRole === "admin") {
next({ status: 422, message: "Role name can not be admin" });
} else if (trimmedRole.length > 32) {
next({
status: 422,
message: "Role name can not be longer than 32 chars",
});
} else {
req.body.role_name = trimmedRole;
next();
}
}
};

module.exports = {
restricted,
checkUsernameExists,
validateRoleName,
only,
};

/* RESTRICTED
If the user does not provide a token in the Authorization header:
status 401
{
Expand All @@ -16,10 +94,8 @@ const restricted = (req, res, next) => {

Put the decoded token in the req object, to make life easier for middlewares downstream!
*/
}

const only = role_name => (req, res, next) => {
/*
/*ONLY
If the user does not provide a token in the Authorization header with a role_name
inside its payload matching the role_name passed to this function as its argument:
status 403
Expand All @@ -29,22 +105,17 @@ const only = role_name => (req, res, next) => {

Pull the decoded token from the req object, to avoid verifying it again!
*/
}

// next();

const checkUsernameExists = (req, res, next) => {
/*
/* CHECK USERNAME EXISTS
If the username in req.body does NOT exist in the database
status 401
{
"message": "Invalid credentials"
}
*/
}


const validateRoleName = (req, res, next) => {
/*
/* VALIDATE ROLE NAME
If the role_name in the body is valid, set req.role_name to be the trimmed string and proceed.

If role_name is missing from req.body, or if after trimming it is just an empty string,
Expand All @@ -62,11 +133,3 @@ const validateRoleName = (req, res, next) => {
"message": "Role name can not be longer than 32 chars"
}
*/
}

module.exports = {
restricted,
checkUsernameExists,
validateRoleName,
only,
}
56 changes: 45 additions & 11 deletions api/auth/auth-router.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,49 @@
const router = require("express").Router();
const { checkUsernameExists, validateRoleName } = require('./auth-middleware');
const { JWT_SECRET } = require("../secrets"); // use this secret!
const bcrypt = require("bcryptjs");
const Users = require("./../users/users-model");
const jwt = require("jsonwebtoken");
const { checkUsernameExists, validateRoleName } = require("./auth-middleware");
const { JWT_SECRET } = require("../secrets");

router.post("/register", validateRoleName, (req, res, next) => {
/**
router.post("/register", validateRoleName, async (req, res, next) => {
let user = req.body;

const rounds = process.env.BCRYPT_ROUNDS || 8;
const hash = bcrypt.hashSync(user.password, rounds);

user.password = hash;

try {
const newUser = await Users.add(user);
res.status(201).json(newUser);
} catch (err) {
next(err);
}
});

router.post("/login", checkUsernameExists, (req, res, next) => {
const { username, password } = req.body;
const { userFromDb } = req;

if (bcrypt.compareSync(password, userFromDb.password)) {
const payload = {
subject: userFromDb.user_id,
username: userFromDb.username,
role_name: userFromDb.role_name,
};
const options = {
expiresIn: "1d",
};
const token = jwt.sign(payload, JWT_SECRET, options);
res.status(200).json({ message: `${username} is back!`, token });
} else {
next({ status: 401, message: "Invalid credentials" });
}
});

module.exports = router;

/**
[POST] /api/auth/register { "username": "anna", "password": "1234", "role_name": "angel" }

response:
Expand All @@ -14,11 +54,8 @@ router.post("/register", validateRoleName, (req, res, next) => {
"role_name": "angel"
}
*/
});


router.post("/login", checkUsernameExists, (req, res, next) => {
/**
/**
[POST] /api/auth/login { "username": "sue", "password": "1234" }

response:
Expand All @@ -37,6 +74,3 @@ router.post("/login", checkUsernameExists, (req, res, next) => {
"role_name": "admin" // the role of the authenticated user
}
*/
});

module.exports = router;
8 changes: 5 additions & 3 deletions api/secrets/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module.exports = {
JWT_SECRET: process.env.JWT_SECRET || "shh",
};


/**
Fix this module so other modules can require JWT_SECRET into them.
Use the || operator to fall back to the string "shh" to handle the situation
Expand All @@ -6,6 +11,3 @@
If no fallback is provided, TESTS WON'T WORK and other
developers cloning this repo won't be able to run the project as is.
*/
module.exports = {

}
95 changes: 61 additions & 34 deletions api/users/users-model.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,62 @@
const db = require('../../data/db-config.js');
const db = require("../../data/db-config.js");

function find() {
/**
async function find() {
const users = db("users as u")
.select("u.user_id", "u.username", "r.role_name")
.join("roles as r", "r.role_id", "u.role_id");

return users;
}

async function findBy(filter) {
const user = await db("users as u")
.select("u.user_id", "u.username", "u.password", "r.role_name")
.join("roles as r", "r.role_id", "u.role_id")
.where(filter);

return user;
}

async function findById(user_id) {
const user = await db("users as u")
.select("u.user_id", "u.username", "r.role_name")
.join("roles as r", "r.role_id", "u.role_id")
.where("user_id", user_id);

return user[0];
}

async function add({ username, password, role_name }) {
// done for you
let created_user_id;
await db.transaction(async (trx) => {
let role_id_to_use;
const [role] = await trx("roles").where("role_name", role_name);
if (role) {
role_id_to_use = role.role_id;
} else {
const [role_id] = await trx("roles").insert({ role_name: role_name });
role_id_to_use = role_id;
}
const [user_id] = await trx("users").insert({
username,
password,
role_id: role_id_to_use,
});
created_user_id = user_id;
});

return findById(created_user_id);
}

module.exports = {
add,
find,
findBy,
findById,
};

/** FIND
You will need to join two tables.
Resolves to an ARRAY with all users.

Expand All @@ -18,10 +73,8 @@ function find() {
}
]
*/
}

function findBy(filter) {
/**
/** FINDBY
You will need to join two tables.
Resolves to an ARRAY with all users that match the filter condition.

Expand All @@ -34,10 +87,8 @@ function findBy(filter) {
}
]
*/
}

function findById(user_id) {
/**
/** FINDBYID
You will need to join two tables.
Resolves to the user with the given user_id.

Expand All @@ -47,9 +98,8 @@ function findById(user_id) {
"role_name": "instructor"
}
*/
}

/**
/** ADD
Creating a user requires a single insert (into users) if the role record with the given
role_name already exists in the db, or two inserts (into roles and then into users)
if the given role_name does not exist yet.
Expand All @@ -67,26 +117,3 @@ function findById(user_id) {
"role_name": "team lead"
}
*/
async function add({ username, password, role_name }) { // done for you
let created_user_id
await db.transaction(async trx => {
let role_id_to_use
const [role] = await trx('roles').where('role_name', role_name)
if (role) {
role_id_to_use = role.role_id
} else {
const [role_id] = await trx('roles').insert({ role_name: role_name })
role_id_to_use = role_id
}
const [user_id] = await trx('users').insert({ username, password, role_id: role_id_to_use })
created_user_id = user_id
})
return findById(created_user_id)
}

module.exports = {
add,
find,
findBy,
findById,
};
Loading