-
Notifications
You must be signed in to change notification settings - Fork 26
hw week 5 #9
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
base: master
Are you sure you want to change the base?
hw week 5 #9
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| const mongoose = require('mongoose') | ||
|
|
||
| const schema = new mongoose.Schema({ | ||
| username: { | ||
| type: String, | ||
| required: true, | ||
| }, | ||
| password: { | ||
| type: String, | ||
| min: 8 | ||
| }, | ||
| admin: { | ||
| type: Boolean, | ||
| default: false | ||
| } | ||
| }, { timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } }) | ||
|
|
||
| module.exports = mongoose.model('User', schema) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| const { SECRET_KEY } = process.env | ||
| const router = require('express').Router() | ||
| const bcrypt = require('bcrypt') | ||
| const jsonwebtoken = require('jsonwebtoken') | ||
| const User = require('../models/user') | ||
|
|
||
| router.post('/signup', async (req, res, next) => { | ||
| const status = 200 | ||
| try { | ||
| const { username, password } = req.body | ||
| const user = await User.findOne({ username }) | ||
| if (user) throw new Error (`User already exists.`) | ||
|
|
||
| //Create User, if user does not yet exist. | ||
| const saltrounds = 12 | ||
| const hashPass = await bcrypt.hash(password, saltrounds) | ||
| const response = await User.create({username, password: hashPass}) | ||
|
|
||
| res.status(status).json({ status, response }) | ||
|
|
||
| } catch (e) { | ||
| console.error(e) | ||
|
|
||
| const error = new Error('User already exists or does not meet validation requirements.') | ||
| error.status = 422 | ||
| next(error) | ||
|
|
||
| res.status(status).json({ status, message }) | ||
|
|
||
| } | ||
|
|
||
| }) | ||
|
|
||
| router.post('/login', async (req, res, next) => { | ||
|
|
||
| try { | ||
| const { username, password } = req.body | ||
| const user = await User.findOne({ username }) | ||
| if (!user) throw new Error('Username could not be found.') | ||
|
|
||
| const validPassword = await bcrypt.compare(password, user.password) | ||
| if (!validPassword) throw new Error('Password is incorrect.') | ||
|
|
||
|
|
||
| // JWT Create | ||
| const status = 201 | ||
| const payload = { id: user._id} | ||
| const options = { expiresIn: '1 hour'} | ||
| const jwt = jsonwebtoken.sign(payload, SECRET_KEY, options) | ||
| res.status(status).json({status, jwt}) | ||
|
|
||
| } catch (e) { | ||
| status = 400 | ||
| console.error(e) | ||
| const error = new Error('Unable to log in.') | ||
| error.status = status | ||
|
|
||
| res.status(status).json({ status, message }) | ||
|
|
||
| } | ||
|
|
||
|
|
||
| }) | ||
|
|
||
| router.patch('/:id/permissions', async (req, res, next) => { | ||
| try { | ||
| validToken = tokenValidate(req); | ||
| const user = await User.findOne({ _id: validToken.id }) | ||
|
|
||
| //Check for valid token | ||
| if (!tokenValidate) { res.status(401).json({ status, response: 'Token is not valid.' })} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be |
||
|
|
||
| //Check if admin value is boolean | ||
| if (!typeof req.body.admin === "boolean"){ res.status(400).json({ status, response: 'Admin value is not boolean.' })} | ||
|
|
||
| //Check if Admin | ||
| if (!user.admin == true) { res.status(400).json({ status, response: 'User is not an Admin.' }) | ||
|
|
||
| //Try and see if user can be found, | ||
| if (!user == null) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This statement is a bit odd. I think what you want here is |
||
| user.admin = req.body.admin | ||
| const user = await User.save | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be: await user.save() |
||
| } else { res.status(400).json({ status, response: 'User does not exist.' })} | ||
|
|
||
| } | ||
| } catch (e) { | ||
| status = 422 | ||
| console.error(e) | ||
| const error = new Error('An error occured.') | ||
| error.status = status | ||
| res.status(status).json({ status }) | ||
|
|
||
| } | ||
|
|
||
| }) | ||
|
|
||
|
|
||
| function tokenValidate(req) { | ||
| const token = req.headers.authorization.split('Bearer ')[1] | ||
| const payload = jsonwebtoken.verify(token, SECRET_KEY) | ||
|
|
||
| return payload | ||
| } | ||
|
|
||
| module.exports = router | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,18 @@ | ||
| const router = require('express').Router() | ||
| const Book = require('../models/book') | ||
| const User = require('../models/user') | ||
|
|
||
| router.get('/', async (req, res, next) => { | ||
| const status = 200 | ||
| const response = await Book.find().select('-__v') | ||
|
|
||
|
|
||
| validToken = tokenValidate(req); | ||
| const user = await User.findOne({ _id: validToken.id }) | ||
| if (user.admin == true) { | ||
|
|
||
| const status = 200 | ||
| const response = await Book.find().select('-__v') | ||
| } else { | ||
| status = 401 | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be surprised if this worked? For this route, it's OK to let anyone request this information. But, it also looks like |
||
| res.json({ status, response }) | ||
| }) | ||
|
|
||
|
|
@@ -26,53 +34,89 @@ router.get('/:id', async (req, res, next) => { | |
|
|
||
| // You should only be able to create a book if the user is an admin | ||
| router.post('/', async (req, res, next) => { | ||
| const status = 200 | ||
| try { | ||
| const book = await Book.create(req.body) | ||
| if (!book) throw new Error(`Request body failed: ${JSON.stringify(req.body)}`) | ||
| validToken = tokenValidate(req); | ||
| const user = await User.findOne({ _id: validToken.id }) | ||
|
|
||
| if (!user.admin == true) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is the opposite of what you want. We only want the user to be able to create a book if they are an admin. So, I think we just need |
||
| const status = 200 | ||
| try { | ||
| const book = await Book.create(req.body) | ||
| if (!book) throw new Error(`Request body failed: ${JSON.stringify(req.body)}`) | ||
|
|
||
| const response = await Book.findById(book._id).select('-__v') | ||
| res.json({ status, response }) | ||
| } catch (e) { | ||
| console.error(e) | ||
| const message = 'Failure to create. Please check request body and try again.' | ||
| const error = new Error(message) | ||
| error.status = 400 | ||
| next(error) | ||
| const response = await Book.findById(book._id).select('-__v') | ||
| res.json({ status, response }) | ||
| } catch (e) { | ||
| console.error(e) | ||
| const message = 'Failure to create. Please check request body and try again.' | ||
| const error = new Error(message) | ||
| error.status = 400 | ||
| next(error) | ||
| } | ||
| } else { res.status(401).json({ status, response: 'User is not admin.' })} | ||
| }) | ||
|
|
||
| // You should only be able to reserve a book if a user is logged in | ||
| router.patch('/:id/reserve', async (req, res, next) => { | ||
| const { id } = req.params | ||
| try { | ||
| const book = await Book.findById(id) | ||
| if (!book) { | ||
| const error = new Error(`Invalid Book _id: ${id}`) | ||
| error.message = 404 | ||
| return next(error) | ||
| } | ||
| validToken = tokenValidate(req); | ||
| const user = await User.findOne({ _id: validToken.id }) | ||
| if (validToken) { | ||
| try { | ||
| const book = await Book.findById(id) | ||
| if (!book) { | ||
| const error = new Error(`Invalid Book _id: ${id}`) | ||
| error.message = 404 | ||
| return next(error) | ||
| } | ||
| if (!book.reserved.status == true) { | ||
| book.reserved.status = true | ||
| // Set the reserved memberId to the current user | ||
| await book.save() | ||
|
|
||
| const response = await Book.findById(book._id).select('-__v') | ||
| const status = 200 | ||
| res.json({ status, response }) | ||
| } else { res.status(400).json({ status, response: 'The book is already reserved' })} | ||
|
|
||
| book.reserved.status = true | ||
| // Set the reserved memberId to the current user | ||
| await book.save() | ||
|
|
||
| const response = await Book.findById(book._id).select('-__v') | ||
| const status = 200 | ||
| res.json({ status, response }) | ||
| } catch (e) { | ||
| console.error(e) | ||
| } | ||
| }) | ||
| } catch (e) { | ||
| console.error(e) | ||
| } | ||
| } else { res.status(401).json({ status, response: 'Invalid Token' })} | ||
| }) | ||
|
|
||
| // You should only be able to return a book if the user is logged in | ||
| // and that user is the one who reserved the book | ||
| router.patch('/:id/return', async (req, res, next) => { | ||
| const status = 200 | ||
| const message = 'You must implement this route!' | ||
|
|
||
| console.log(message) | ||
| res.status(status).json({ status, message }) | ||
| validToken = tokenValidate(req); | ||
| const user = await User.findOne({ _id: validToken.id }) | ||
|
|
||
| if (validToken) { | ||
| const status = 200 | ||
| const book = await Book.findById(id) | ||
|
|
||
| // 404 if unable to find book. | ||
| if (!book) { res.status(404).json({ status, response: 'Unable to find book' })} | ||
|
|
||
| // 400 if book is not reserved. | ||
| if (book.reserved.status == false) { res.status(400).json({ status, response: 'The book is not reserved' })} | ||
|
|
||
| // 401 if ID of the user that checked out the book does not match. | ||
| if (tokenValidate._id != book.reserved.memberID) { res.status(401).json({ status, response: 'The book is reserved but not by this user.' })} | ||
|
|
||
| // After validation, set reserved status to false, and memberId to null | ||
| book.reserved.status = false | ||
| reserved.memberId = null | ||
|
|
||
| console.log(message) | ||
| res.status(status).json({ status, message }) | ||
| } else { res.status(401).json({ status, response: 'Invalid Token' })} | ||
| }) | ||
|
|
||
| function tokenValidate(req) { | ||
| const token = req.headers.authorization.split('Bearer ')[1] | ||
| const payload = jsonwebtoken.verify(token, SECRET_KEY) | ||
|
|
||
| return payload | ||
| } | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could just export this function so you don't need to copy it everywhere! |
||
| module.exports = router | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| "env": { | ||
| "MONGO_DB_CONNECTION": "", | ||
| "NODE_ENV": "development", | ||
| "PORT": 5000 | ||
| "PORT": 5000, | ||
| "SECRET_KEY": "" | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll want to give the same errors in either case. Otherwise, it's easier for someone to "guess" at a valid username.