정민우

Merge branch 'backend' into 'master'

[New] Auth, Log 구현

Backend Auth, Log 구현

See merge request !1
...@@ -3,7 +3,8 @@ let cookieParser = require('cookie-parser') ...@@ -3,7 +3,8 @@ let cookieParser = require('cookie-parser')
3 var cors = require('cors') 3 var cors = require('cors')
4 var corsConfig = require('./config/cors') 4 var corsConfig = require('./config/cors')
5 5
6 -let indexRouter = require('./routes/index') 6 +const indexRouter = require('./routes/index')
7 +const authRouter = require('./routes/authRouter')
7 8
8 let app = express() 9 let app = express()
9 app.use(cors(corsConfig)) 10 app.use(cors(corsConfig))
...@@ -13,6 +14,7 @@ app.use(express.urlencoded({ extended: false })) ...@@ -13,6 +14,7 @@ app.use(express.urlencoded({ extended: false }))
13 app.use(cookieParser()) 14 app.use(cookieParser())
14 15
15 app.use('/', indexRouter) 16 app.use('/', indexRouter)
17 +app.use('/', authRouter)
16 18
17 app.use(function(req, res) { 19 app.use(function(req, res) {
18 res.status(400) 20 res.status(400)
......
...@@ -7,6 +7,8 @@ const database = process.env.MYSQL_DATABASE || 'root' ...@@ -7,6 +7,8 @@ const database = process.env.MYSQL_DATABASE || 'root'
7 const host = process.env.MYSQL_HOST || '127.0.0.1' 7 const host = process.env.MYSQL_HOST || '127.0.0.1'
8 const port = process.env.MYSQL_PORT || 3306 8 const port = process.env.MYSQL_PORT || 3306
9 9
10 +const JWT_KEY = process.env.JWT_KEY || null
11 +
10 module.exports = { 12 module.exports = {
11 'username': username, 13 'username': username,
12 'password': password, 14 'password': password,
...@@ -14,5 +16,6 @@ module.exports = { ...@@ -14,5 +16,6 @@ module.exports = {
14 'host': host, 16 'host': host,
15 'port': port, 17 'port': port,
16 'dialect': 'mysql', 18 'dialect': 'mysql',
17 - 'operatorsAliases': false 19 + 'operatorsAliases': false,
20 + 'JWT_KEY': JWT_KEY
18 } 21 }
......
1 +const jwt = require('jsonwebtoken')
2 +const bcrypt = require('bcrypt')
3 +const config = require(__dirname + '/../config/config')
4 +
5 +const { User } = require('../models');
6 +const { sendResponse, sendError } = require('../utils/response')
7 +const { checkRequiredExist } = require('../utils/validation')
8 +const { logging } = require('../utils/log')
9 +
10 +exports.login = async (req, res) => {
11 + const required = checkRequiredExist(req.body, ['userId', 'password'])
12 + if (required) {
13 + logging('auth', 'error', { code: 400, message: `missingKey:${required}` }, req)
14 + return sendError(res, 400, `missingKey:${required}`)
15 + }
16 + const userId = req.body.userId
17 + const password = req.body.password
18 +
19 + let token = null
20 + let user = null
21 + let isSuccess = false
22 + let tokenInfo = {
23 + userInfo: {
24 + id: null,
25 + },
26 + tokenConfig: {
27 + expiresIn: '24h',
28 + issuer: 'OSS',
29 + },
30 + }
31 +
32 + let match = false
33 + try {
34 + user = await User.findOne({
35 + where: {
36 + userId: userId
37 + }
38 + })
39 + if (user) {
40 + match = await bcrypt.compare(password, user.password)
41 + }
42 + } catch (error) {
43 + logging('auth', 'error', { code: 500, message: error.message }, req)
44 + return sendError(res, 500, error.message)
45 + }
46 +
47 + if (match) {
48 + tokenInfo.userInfo.id = user.id
49 + isSuccess = true
50 + }
51 + else {
52 + logging('auth', 'error', { code: 401, message: 'Auth Failed' }, req)
53 + return sendError(res, 401, 'Auth Failed')
54 + }
55 +
56 + if (isSuccess === true) { //성공할 경우 토큰 발행
57 + token = jwt.sign (tokenInfo.userInfo, config.JWT_KEY, tokenInfo.tokenConfig)
58 + logging('auth', 'access', { user: tokenInfo.userInfo.id },req)
59 + return res.status(200).json({ success: true, access_token: token })
60 + }
61 +}
62 +
63 +exports.userInfo = async (req, res) => {
64 + const id = req.decoded.id
65 + if (!id) {
66 + return sendError(res, 401, 'InvalidToken')
67 + }
68 + let user = null
69 + try {
70 + user = await User.findOne ({
71 + where: {
72 + id
73 + }
74 + })
75 + } catch (error) {
76 + return sendError(res, 500, error.message)
77 + }
78 + if (user) {
79 + sendResponse(res, user, 200)
80 + } else {
81 + return sendError(res, 404, 'NoUserFound')
82 +
83 + }
84 +}
85 +
86 +exports.adminTest = async (req, res) => {
87 + return sendResponse(res, "Just Test", 200, "Test OK")
88 +}
89 +
90 +exports.isAdmin = async (userId) => {
91 + const user = await User.findByPk(userId)
92 + return (user && await user.isAdmin)
93 +}
1 +const jwt = require('jsonwebtoken')
2 +const config = require(__dirname + '/../config/config')
3 +
4 +const { sendError } = require('../utils/response')
5 +const { isAdmin } = require('../controllers/authController')
6 +const { logging } = require('../utils/log')
7 +
8 +exports.memberOnly = (req, res, next) => {
9 + const token = req.headers.authorization
10 + try {
11 + req.decoded = jwt.verify(token, config.JWT_KEY)
12 + return next()
13 + } catch (error) {
14 + if (error.name === 'TokenExpiredError') {
15 + logging('auth', 'error', { code: 419, message: `TokenExpired` }, req)
16 + return sendError(res, 419,'TokenExpired')
17 +
18 + } else {
19 + logging('auth', 'error', { code: 401, message: `InvalidToken` }, req)
20 + return sendError(res, 401, 'InvalidToken')
21 + }
22 + }
23 +}
24 +
25 +exports.guestOnly = (req, res, next) => {
26 + const token = req.headers.authorization
27 + if (typeof token === undefined) {
28 + return next()
29 + } else {
30 + try {
31 + const decoded = jwt.verify(token, config.JWT_KEY)
32 + if (decoded !== null) {
33 + logging('auth', 'error', { code: 403, message: `GuestOnly` }, req)
34 + return sendError(res, 403, 'GuestOnly')
35 + } else {
36 + return next()
37 + }
38 + } catch (error) {
39 + return next()
40 + }
41 + }
42 +}
43 +
44 +exports.adminOnly = async (req, res, next) => {
45 + const token = req.headers.authorization
46 + let auth = false
47 + let userId = null
48 +
49 + try{
50 + req.decoded = jwt.verify(token, config.JWT_KEY)
51 + userId = req.decoded.id
52 + } catch (error) {
53 + if (error.name === 'TokenExpiredError') {
54 + logging('auth', 'error', { code: 419, message: `TokenExpired` }, req)
55 + return sendError(res, 419, 'TokenExpired')
56 + } else {
57 + logging('auth', 'error', { code: 401, message: `InvalidToken` }, req)
58 + return sendError(res, 401, 'InvalidToken')
59 + }
60 + }
61 +
62 + auth = await isAdmin(userId)
63 + if (auth) {
64 + next()
65 + } else {
66 + logging('auth', 'error', { code: 403, message: `Unauthoirzed Access` }, req)
67 + return sendError(res, 403, 'Unauthoirzed Access')
68 + }
69 +}
1 +'use strict';
2 +const bcrypt = require('bcrypt')
3 +const path = require('path')
4 +require('dotenv').config({path: path.join(__dirname, "../.env")})
5 +
6 +module.exports = {
7 + up: async (queryInterface, Sequelize) => {
8 + return Promise.all([
9 + await queryInterface.createTable('Users', {
10 + id: {
11 + allowNull: false,
12 + autoIncrement: true,
13 + primaryKey: true,
14 + type: Sequelize.INTEGER
15 + },
16 + userId: {
17 + type: Sequelize.STRING(191),
18 + unique:true
19 + },
20 + password: {
21 + type: Sequelize.STRING(191)
22 + },
23 + name: {
24 + type: Sequelize.STRING(191)
25 + },
26 + phone: {
27 + type: Sequelize.STRING(31)
28 + },
29 + email: {
30 + type: Sequelize.STRING(191)
31 + },
32 + isAdmin: {
33 + type:Sequelize.BOOLEAN,
34 + defalutValue: false,
35 + allowNull: false
36 + },
37 + createdAt: {
38 + type: Sequelize.DATE,
39 + allowNull: false
40 + },
41 + updatedAt: {
42 + type: Sequelize.DATE,
43 + allowNull: false
44 + },
45 + deletedAt: {
46 + type: Sequelize.DATE
47 + }
48 + }, {
49 + charset: 'utf8mb4',
50 + collate: 'utf8mb4_unicode_ci'
51 + }),
52 + await queryInterface.bulkInsert('Users',
53 + [
54 + {
55 + id: 1,
56 + name: '관리자',
57 + userId: process.env.INITIAL_ADMIN_ID,
58 + password: await bcrypt.hash(process.env.INITIAL_ADMIN_PW, 10),
59 + isAdmin: true,
60 + createdAt: new Date(),
61 + updatedAt: new Date()
62 + }
63 + ])
64 +
65 + ])
66 +
67 + },
68 + down: async (queryInterface, Sequelize) => {
69 + await queryInterface.dropTable('Users');
70 + }
71 +};
1 +'use strict';
2 +module.exports = {
3 + up: async (queryInterface, Sequelize) => {
4 + await queryInterface.createTable('Logs', {
5 + id: {
6 + allowNull: false,
7 + autoIncrement: true,
8 + primaryKey: true,
9 + type: Sequelize.INTEGER
10 + },
11 + user: {
12 + type: Sequelize.INTEGER
13 + },
14 + location: {
15 + type: Sequelize.STRING(191),
16 + },
17 + module: {
18 + type: Sequelize.STRING(191),
19 + },
20 + actionType: {
21 + type: Sequelize.STRING(191)
22 + },
23 + data: {
24 + type: Sequelize.JSON
25 + },
26 + ipAddress:{
27 + type: Sequelize.STRING(191)
28 + },
29 + createdAt: {
30 + allowNull: false,
31 + type: Sequelize.DATE
32 + }
33 + },{
34 + charset: 'utf8mb4',
35 + collate: 'utf8mb4_unicode_ci'
36 + })
37 + },
38 + down: async (queryInterface, Sequelize) => {
39 + await queryInterface.dropTable('Logs');
40 + }
41 +};
1 -'use strict' 1 +'use strict';
2 2
3 const fs = require('fs') 3 const fs = require('fs')
4 const path = require('path') 4 const path = require('path')
5 const Sequelize = require('sequelize') 5 const Sequelize = require('sequelize')
6 +
6 const basename = path.basename(__filename) 7 const basename = path.basename(__filename)
7 const config = require(__dirname + '/../config/config') 8 const config = require(__dirname + '/../config/config')
8 -const db = {} 9 +let db = {}
9 10
10 let sequelize 11 let sequelize
11 if (config.use_env_variable) { 12 if (config.use_env_variable) {
...@@ -13,6 +14,8 @@ if (config.use_env_variable) { ...@@ -13,6 +14,8 @@ if (config.use_env_variable) {
13 } else { 14 } else {
14 sequelize = new Sequelize(config.database, config.username, config.password, config) 15 sequelize = new Sequelize(config.database, config.username, config.password, config)
15 } 16 }
17 +db.sequelize=sequelize
18 +db.Sequelize=Sequelize
16 19
17 fs 20 fs
18 .readdirSync(__dirname) 21 .readdirSync(__dirname)
...@@ -20,7 +23,7 @@ fs ...@@ -20,7 +23,7 @@ fs
20 return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js') 23 return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
21 }) 24 })
22 .forEach(file => { 25 .forEach(file => {
23 - const model = sequelize['import'](path.join(__dirname, file)) 26 + let model = require(path.join(__dirname, file))(sequelize, Sequelize)
24 db[model.name] = model 27 db[model.name] = model
25 }) 28 })
26 29
...@@ -30,7 +33,4 @@ Object.keys(db).forEach(modelName => { ...@@ -30,7 +33,4 @@ Object.keys(db).forEach(modelName => {
30 } 33 }
31 }) 34 })
32 35
33 -db.sequelize = sequelize
34 -db.Sequelize = Sequelize
35 -
36 module.exports = db 36 module.exports = db
......
1 +'use strict'
2 +const { sequelize } = require('.')
3 +const { Model } = require('sequelize')
4 +module.exports = (sequelize, DataTypes) => {
5 + class Log extends Model {}
6 +
7 + Log.init({
8 + id: {
9 + allowNull: false,
10 + autoIncrement: true,
11 + primaryKey: true,
12 + type: DataTypes.INTEGER
13 + },
14 + user: {
15 + type: DataTypes.INTEGER
16 + },
17 + location: {
18 + type: DataTypes.STRING(191),
19 + },
20 + module: {
21 + type: DataTypes.STRING(191),
22 + },
23 + actionType: {
24 + type: DataTypes.STRING(191)
25 + },
26 + data: {
27 + type: DataTypes.JSON
28 + },
29 + after: {
30 + type: DataTypes.JSON
31 + },
32 + ipAddress:{
33 + type: DataTypes.STRING(191)
34 + },
35 + createdAt: {
36 + allowNull: false,
37 + type: DataTypes.DATE
38 + }
39 + }, {
40 + timestamps: true,
41 + updatedAt:false,
42 + sequelize,
43 + modelName: 'Log',
44 + });
45 + return Log;
46 +};
1 +'use strict'
2 +const { sequelize } = require('.')
3 +const { Model } = require('sequelize')
4 +module.exports = (sequelize, DataTypes) => {
5 + class User extends Model { }
6 +
7 + User.init({
8 + id: {
9 + type: DataTypes.INTEGER,
10 + primaryKey: true,
11 + autoIncrement: true
12 + },
13 + userId: {
14 + type: DataTypes.STRING(191),
15 + unique: true
16 + },
17 + password: {
18 + type: DataTypes.STRING(191),
19 + },
20 + name: {
21 + type: DataTypes.STRING(191),
22 + },
23 + phone: {
24 + type: DataTypes.STRING(31),
25 + },
26 + email: {
27 + type: DataTypes.STRING(191),
28 + },
29 + isAdmin: {
30 + type: DataTypes.BOOLEAN,
31 + allowNull: false,
32 + defaultValue: false,
33 + }
34 + }, {
35 + sequelize,
36 + paranoid: true,
37 + modelName: 'User'
38 + })
39 +
40 + return User
41 +}
This diff could not be displayed because it is too large.
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
6 "start": "sequelize db:migrate && node ./bin/www --env-update" 6 "start": "sequelize db:migrate && node ./bin/www --env-update"
7 }, 7 },
8 "dependencies": { 8 "dependencies": {
9 + "bcrypt": "^5.0.1",
9 "cookie-parser": "~1.4.4", 10 "cookie-parser": "~1.4.4",
10 "cors": "^2.8.5", 11 "cors": "^2.8.5",
11 "debug": "~2.6.9", 12 "debug": "~2.6.9",
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
17 "devDependencies": { 18 "devDependencies": {
18 "eslint": "^6.8.0", 19 "eslint": "^6.8.0",
19 "eslint-plugin-node": "^11.1.0", 20 "eslint-plugin-node": "^11.1.0",
21 + "jsonwebtoken": "^8.5.1",
20 "sequelize-cli": "^6.2.0" 22 "sequelize-cli": "^6.2.0"
21 } 23 }
22 } 24 }
......
1 +const express = require ('express');
2 +const router = express.Router();
3 +
4 +const authController = require ('../controllers/authController')
5 +const { guestOnly, memberOnly, adminOnly } = require ('../middlewares/auth')
6 +
7 +router.post('/login', guestOnly, authController.login)
8 +router.post('/user/info', memberOnly, authController.userInfo)
9 +router.post('/admin_test', adminOnly, authController.adminTest)
10 +
11 +module.exports = router
1 +const { Log } = require('../models')
2 +const url = require('url')
3 +
4 +exports.logging = async (module, actionType ,data, req) => {
5 +
6 + let logData = {
7 + module,
8 + actionType,
9 + data
10 + }
11 + if (req) {
12 + location = url.format({
13 + protocol: req.protocol,
14 + host: req.get('host'),
15 + pathname: req.originalUrl
16 + })
17 + if( Object.keys(req).includes('decoded') ) {
18 + logData.user = req.decoded.id
19 + }
20 + logData.location = location
21 + logData.ipAddress = req.ip
22 + }
23 + try {
24 + await Log.create(logData)
25 + } catch (error) {
26 + throw error
27 + }
28 +}
1 +exports.sendError = function (res, code, message) {
2 + let result = {
3 + success: false,
4 + code,
5 + message,
6 + data: null
7 + }
8 + return res.status(200).json(result)
9 +
10 +}
11 +
12 +exports.sendResponse = function (res, data, code, message) {
13 + code = code || 200
14 + const result = {
15 + success: true,
16 + code,
17 + message,
18 + data
19 + }
20 + return res.status(code).json(result)
21 +}
1 +exports.checkRequiredExist = (list, required) => {
2 + try {
3 + for (let key of required) {
4 + if (typeof list[key] === 'undefined' || list[key] === null) {
5 + return key
6 + }
7 + }
8 + return false
9 + } catch (error) {
10 + throw error
11 + }
12 +}
13 +
14 +exports.setValues = (bucket, keys) => {
15 + let result = { }
16 + try {
17 + for (let key of keys) {
18 + if (typeof bucket[key] !== 'undefined' && bucket[key] !== null) {
19 + result[key] = bucket[key]
20 + }
21 + }
22 + return result
23 + } catch (error) {
24 + throw error
25 + }
26 +}