From c1d16d720d2381baee89d7ba22ab8c226f24cc9b Mon Sep 17 00:00:00 2001 From: "Yoon, Daeki" Date: Sat, 26 Sep 2020 11:29:36 +0900 Subject: [PATCH] =?UTF-8?q?auth,=20user=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src/server/auth/auth.controller.js | 54 +++++++++++++++++++- src/server/auth/auth.routes.js | 12 +++++ src/server/config/config.js | 1 + src/server/express.js | 11 +++- src/server/user/user.controller.js | 81 ++++++++++++++++++++++++++++-- src/server/user/user.model.js | 4 ++ src/server/user/user.routes.js | 12 ++++- yarn.lock | 10 ++++ 9 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 src/server/auth/auth.routes.js diff --git a/package.json b/package.json index f3ce225..f886e94 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "body-parser": "^1.19.0", "express": "^4.17.1", "express-jwt": "^6.0.0", + "formidable": "^1.2.2", "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.20", "mongodb": "^3.6.2", "mongoose": "^5.10.7", "nodemon": "^2.0.4" diff --git a/src/server/auth/auth.controller.js b/src/server/auth/auth.controller.js index e285418..a43e1dd 100644 --- a/src/server/auth/auth.controller.js +++ b/src/server/auth/auth.controller.js @@ -1,15 +1,65 @@ import jwt from 'jsonwebtoken' +import expressJwt from 'express-jwt' +import User from '../user/user.model.js' +import config from '../config/config.js' const signin = async (req, res) => { try { - let user + let user = await User.findOne({ 'email': req.body.email }) + if (!user) { + return res.status(401).json({ + error: 'User not found' + }) + } + + if (!user.authenticate(req.body.password)) { + return res.status(401).json({ + error: "Email and password don't match" + }) + } + + const token = jwt.sign({ _id: user._id }, config.jwtSecret) + + return res.json({ + token, + user: { + _id: user._id, + name: user.name, + email: user.email, + } + }) } catch (error) { return res.status(400).json({ - error: 'User not found' + error: 'Could not sign in' + }) + } +} + +const signout = (req, res) => { + return res.json({ + message: 'Signed out' + }) +} + +const requireSignin = expressJwt({ + secret: config.jwtSecret, + requestProperty: 'auth', + algorithms: ['HS256'] +}) + +const hasAuthorization = (req, res, next) => { + const authorized = req.profile && req.auth && req.profile._id === req.auth._id + if (!authorized) { + return res.status(403).json({ + error: 'User is not authorized' }) } + next() } export default { signin, + signout, + requireSignin, + hasAuthorization, } \ No newline at end of file diff --git a/src/server/auth/auth.routes.js b/src/server/auth/auth.routes.js new file mode 100644 index 0000000..28c0d7f --- /dev/null +++ b/src/server/auth/auth.routes.js @@ -0,0 +1,12 @@ +import express from 'express' +import authCtrl from './auth.controller' + +const router = express.Router() + +router.route('/auth/signin') + .post(authCtrl.signin) + +router.route('/auth/signout') + .get(authCtrl.signout) + +export default router \ No newline at end of file diff --git a/src/server/config/config.js b/src/server/config/config.js index fc8add3..5978fc9 100644 --- a/src/server/config/config.js +++ b/src/server/config/config.js @@ -1,6 +1,7 @@ const config = { env: process.env.NODE_ENV || 'development', port: process.env.PORT || 3000, + jwtSecret: 'My_Secure_Screte', mongoUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/quizcompetition' } diff --git a/src/server/express.js b/src/server/express.js index c032edd..962e4f5 100644 --- a/src/server/express.js +++ b/src/server/express.js @@ -10,8 +10,15 @@ app.use(bodyParser.urlencoded({ extended: true })) app.use('/', userRoutes) app.use((err, req, res, next) => { - if (err) { - console.log('Error in Express', err) + if (err.name === 'UnauthorizedError') { + res.status(401).json({ + error: err.name + ': ' + err.message + }) + } else if (err) { + res.status(400).json({ + error: err.name + ': ' + err.message + }) + console.log(err) } }) diff --git a/src/server/user/user.controller.js b/src/server/user/user.controller.js index 0452515..ca70b15 100644 --- a/src/server/user/user.controller.js +++ b/src/server/user/user.controller.js @@ -1,11 +1,14 @@ -import User from './user.model.js'; +import User from './user.model.js' +import formidable from 'formidable' +import extend from 'lodash/extend' +import fs from 'fs' const create = async (req, res) => { const user = new User(req.body) try { await user.save() return res.json({ - message: 'Succefully signed up!' + message: 'Successfully signed up!' }) } catch (error) { return res.status(400).json({ @@ -20,7 +23,75 @@ const list = async (req, res) => { return res.json(users) } catch (error) { return res.status(400).json({ - error: 'User not found' + error: 'Users not found' + }) + } +} + +const read = (req, res) => { + req.profile.hashedPassword = undefined + req.profile.salt = undefined + return res.json(req.profile) +} + +const update = async (req, res) => { + let form = new formidable.IncomingForm() + form.keepExtensions = true + form.parse(req, async (err, fields, files) => { + if (err) { + return res.status(400).json({ + error: 'Photo could not be uploaded' + }) + } + let user = req.profile + user = extend(user, fields) + user.updated = new Date() + if (files.photo) { + user.photo.data = fs.readFileSync(files,photo.path) + user.photo.contentType = files.photo.type + } + + try { + await user.save() + user.hashedPassword = undefined + user.salt = undefined + res.json(user) + } catch (error) { + return res.status(400).json({ + error: 'User save error' + }) + } + }) +} + +const remove = async (req, res) => { + try { + let user = req.profile + let deletedUser = await user.remove() + deletedUser.hashedPassword = undefined + deletedUser.salt = undefined + res.json(deletedUser) + } catch (error) { + return res.status(400).json({ + error: 'User delete error' + }) + } +} + +const userById = async (req, res, next, id) => { + try { + let user = await User.findById(id) + .exec() + if (!user) { + return res.status(400).json({ + error: 'User not found' + }) + } + req.profile = user + next() + } catch (error) { + return res.status(400).json({ + error: 'Could not retrieve user' }) } } @@ -28,4 +99,8 @@ const list = async (req, res) => { export default { create, list, + read, + update, + remove, + userById, } \ No newline at end of file diff --git a/src/server/user/user.model.js b/src/server/user/user.model.js index 41bc1f4..fc9c398 100644 --- a/src/server/user/user.model.js +++ b/src/server/user/user.model.js @@ -24,6 +24,10 @@ const UserSchema = new mongoose.Schema({ required: 'Password is required' }, salt: String, + photo: { + data: Buffer, + contentType: String, + }, }) UserSchema.virtual('password') diff --git a/src/server/user/user.routes.js b/src/server/user/user.routes.js index 4bce763..0fa761e 100644 --- a/src/server/user/user.routes.js +++ b/src/server/user/user.routes.js @@ -1,10 +1,18 @@ import express from 'express' +import authCtrl from '../auth/auth.controller.js' import userCtrl from './user.controller.js' const router = express.Router() router.route('/api/users') -.get(userCtrl.list) -.post(userCtrl.create) + .get(userCtrl.list) + .post(userCtrl.create) + +router.route('/api/users/:userId') + .get(authCtrl.requireSignin, userCtrl.read) + .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update) + .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove) + +router.param('userId', userCtrl.userById) export default router \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c4f3dd3..b9ed6f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -518,6 +518,11 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +formidable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" + integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -908,6 +913,11 @@ lodash.set@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= +lodash@^4.17.20: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" -- GitLab