정민우

Merge branch 'backend' into 'master'

[New] Auth, Log 구현

Backend Auth, Log 구현

See merge request !1
......@@ -3,7 +3,8 @@ let cookieParser = require('cookie-parser')
var cors = require('cors')
var corsConfig = require('./config/cors')
let indexRouter = require('./routes/index')
const indexRouter = require('./routes/index')
const authRouter = require('./routes/authRouter')
let app = express()
app.use(cors(corsConfig))
......@@ -13,6 +14,7 @@ app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use('/', indexRouter)
app.use('/', authRouter)
app.use(function(req, res) {
res.status(400)
......
......@@ -7,6 +7,8 @@ const database = process.env.MYSQL_DATABASE || 'root'
const host = process.env.MYSQL_HOST || '127.0.0.1'
const port = process.env.MYSQL_PORT || 3306
const JWT_KEY = process.env.JWT_KEY || null
module.exports = {
'username': username,
'password': password,
......@@ -14,5 +16,6 @@ module.exports = {
'host': host,
'port': port,
'dialect': 'mysql',
'operatorsAliases': false
'operatorsAliases': false,
'JWT_KEY': JWT_KEY
}
......
const jwt = require('jsonwebtoken')
const bcrypt = require('bcrypt')
const config = require(__dirname + '/../config/config')
const { User } = require('../models');
const { sendResponse, sendError } = require('../utils/response')
const { checkRequiredExist } = require('../utils/validation')
const { logging } = require('../utils/log')
exports.login = async (req, res) => {
const required = checkRequiredExist(req.body, ['userId', 'password'])
if (required) {
logging('auth', 'error', { code: 400, message: `missingKey:${required}` }, req)
return sendError(res, 400, `missingKey:${required}`)
}
const userId = req.body.userId
const password = req.body.password
let token = null
let user = null
let isSuccess = false
let tokenInfo = {
userInfo: {
id: null,
},
tokenConfig: {
expiresIn: '24h',
issuer: 'OSS',
},
}
let match = false
try {
user = await User.findOne({
where: {
userId: userId
}
})
if (user) {
match = await bcrypt.compare(password, user.password)
}
} catch (error) {
logging('auth', 'error', { code: 500, message: error.message }, req)
return sendError(res, 500, error.message)
}
if (match) {
tokenInfo.userInfo.id = user.id
isSuccess = true
}
else {
logging('auth', 'error', { code: 401, message: 'Auth Failed' }, req)
return sendError(res, 401, 'Auth Failed')
}
if (isSuccess === true) { //성공할 경우 토큰 발행
token = jwt.sign (tokenInfo.userInfo, config.JWT_KEY, tokenInfo.tokenConfig)
logging('auth', 'access', { user: tokenInfo.userInfo.id },req)
return res.status(200).json({ success: true, access_token: token })
}
}
exports.userInfo = async (req, res) => {
const id = req.decoded.id
if (!id) {
return sendError(res, 401, 'InvalidToken')
}
let user = null
try {
user = await User.findOne ({
where: {
id
}
})
} catch (error) {
return sendError(res, 500, error.message)
}
if (user) {
sendResponse(res, user, 200)
} else {
return sendError(res, 404, 'NoUserFound')
}
}
exports.adminTest = async (req, res) => {
return sendResponse(res, "Just Test", 200, "Test OK")
}
exports.isAdmin = async (userId) => {
const user = await User.findByPk(userId)
return (user && await user.isAdmin)
}
const jwt = require('jsonwebtoken')
const config = require(__dirname + '/../config/config')
const { sendError } = require('../utils/response')
const { isAdmin } = require('../controllers/authController')
const { logging } = require('../utils/log')
exports.memberOnly = (req, res, next) => {
const token = req.headers.authorization
try {
req.decoded = jwt.verify(token, config.JWT_KEY)
return next()
} catch (error) {
if (error.name === 'TokenExpiredError') {
logging('auth', 'error', { code: 419, message: `TokenExpired` }, req)
return sendError(res, 419,'TokenExpired')
} else {
logging('auth', 'error', { code: 401, message: `InvalidToken` }, req)
return sendError(res, 401, 'InvalidToken')
}
}
}
exports.guestOnly = (req, res, next) => {
const token = req.headers.authorization
if (typeof token === undefined) {
return next()
} else {
try {
const decoded = jwt.verify(token, config.JWT_KEY)
if (decoded !== null) {
logging('auth', 'error', { code: 403, message: `GuestOnly` }, req)
return sendError(res, 403, 'GuestOnly')
} else {
return next()
}
} catch (error) {
return next()
}
}
}
exports.adminOnly = async (req, res, next) => {
const token = req.headers.authorization
let auth = false
let userId = null
try{
req.decoded = jwt.verify(token, config.JWT_KEY)
userId = req.decoded.id
} catch (error) {
if (error.name === 'TokenExpiredError') {
logging('auth', 'error', { code: 419, message: `TokenExpired` }, req)
return sendError(res, 419, 'TokenExpired')
} else {
logging('auth', 'error', { code: 401, message: `InvalidToken` }, req)
return sendError(res, 401, 'InvalidToken')
}
}
auth = await isAdmin(userId)
if (auth) {
next()
} else {
logging('auth', 'error', { code: 403, message: `Unauthoirzed Access` }, req)
return sendError(res, 403, 'Unauthoirzed Access')
}
}
'use strict';
const bcrypt = require('bcrypt')
const path = require('path')
require('dotenv').config({path: path.join(__dirname, "../.env")})
module.exports = {
up: async (queryInterface, Sequelize) => {
return Promise.all([
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.STRING(191),
unique:true
},
password: {
type: Sequelize.STRING(191)
},
name: {
type: Sequelize.STRING(191)
},
phone: {
type: Sequelize.STRING(31)
},
email: {
type: Sequelize.STRING(191)
},
isAdmin: {
type:Sequelize.BOOLEAN,
defalutValue: false,
allowNull: false
},
createdAt: {
type: Sequelize.DATE,
allowNull: false
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false
},
deletedAt: {
type: Sequelize.DATE
}
}, {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
}),
await queryInterface.bulkInsert('Users',
[
{
id: 1,
name: '관리자',
userId: process.env.INITIAL_ADMIN_ID,
password: await bcrypt.hash(process.env.INITIAL_ADMIN_PW, 10),
isAdmin: true,
createdAt: new Date(),
updatedAt: new Date()
}
])
])
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Logs', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
user: {
type: Sequelize.INTEGER
},
location: {
type: Sequelize.STRING(191),
},
module: {
type: Sequelize.STRING(191),
},
actionType: {
type: Sequelize.STRING(191)
},
data: {
type: Sequelize.JSON
},
ipAddress:{
type: Sequelize.STRING(191)
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
}
},{
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
})
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Logs');
}
};
'use strict'
'use strict';
const fs = require('fs')
const path = require('path')
const Sequelize = require('sequelize')
const basename = path.basename(__filename)
const config = require(__dirname + '/../config/config')
const db = {}
let db = {}
let sequelize
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config)
sequelize = new Sequelize(process.env[config.use_env_variable], config)
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config)
sequelize = new Sequelize(config.database, config.username, config.password, config)
}
db.sequelize=sequelize
db.Sequelize=Sequelize
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file))
db[model.name] = model
})
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
})
.forEach(file => {
let model = require(path.join(__dirname, file))(sequelize, Sequelize)
db[model.name] = model
})
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db)
}
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize = sequelize
db.Sequelize = Sequelize
module.exports = db
......
'use strict'
const { sequelize } = require('.')
const { Model } = require('sequelize')
module.exports = (sequelize, DataTypes) => {
class Log extends Model {}
Log.init({
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
user: {
type: DataTypes.INTEGER
},
location: {
type: DataTypes.STRING(191),
},
module: {
type: DataTypes.STRING(191),
},
actionType: {
type: DataTypes.STRING(191)
},
data: {
type: DataTypes.JSON
},
after: {
type: DataTypes.JSON
},
ipAddress:{
type: DataTypes.STRING(191)
},
createdAt: {
allowNull: false,
type: DataTypes.DATE
}
}, {
timestamps: true,
updatedAt:false,
sequelize,
modelName: 'Log',
});
return Log;
};
'use strict'
const { sequelize } = require('.')
const { Model } = require('sequelize')
module.exports = (sequelize, DataTypes) => {
class User extends Model { }
User.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.STRING(191),
unique: true
},
password: {
type: DataTypes.STRING(191),
},
name: {
type: DataTypes.STRING(191),
},
phone: {
type: DataTypes.STRING(31),
},
email: {
type: DataTypes.STRING(191),
},
isAdmin: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
}
}, {
sequelize,
paranoid: true,
modelName: 'User'
})
return User
}
This diff could not be displayed because it is too large.
......@@ -6,6 +6,7 @@
"start": "sequelize db:migrate && node ./bin/www --env-update"
},
"dependencies": {
"bcrypt": "^5.0.1",
"cookie-parser": "~1.4.4",
"cors": "^2.8.5",
"debug": "~2.6.9",
......@@ -17,6 +18,7 @@
"devDependencies": {
"eslint": "^6.8.0",
"eslint-plugin-node": "^11.1.0",
"jsonwebtoken": "^8.5.1",
"sequelize-cli": "^6.2.0"
}
}
......
const express = require ('express');
const router = express.Router();
const authController = require ('../controllers/authController')
const { guestOnly, memberOnly, adminOnly } = require ('../middlewares/auth')
router.post('/login', guestOnly, authController.login)
router.post('/user/info', memberOnly, authController.userInfo)
router.post('/admin_test', adminOnly, authController.adminTest)
module.exports = router
const { Log } = require('../models')
const url = require('url')
exports.logging = async (module, actionType ,data, req) => {
let logData = {
module,
actionType,
data
}
if (req) {
location = url.format({
protocol: req.protocol,
host: req.get('host'),
pathname: req.originalUrl
})
if( Object.keys(req).includes('decoded') ) {
logData.user = req.decoded.id
}
logData.location = location
logData.ipAddress = req.ip
}
try {
await Log.create(logData)
} catch (error) {
throw error
}
}
exports.sendError = function (res, code, message) {
let result = {
success: false,
code,
message,
data: null
}
return res.status(200).json(result)
}
exports.sendResponse = function (res, data, code, message) {
code = code || 200
const result = {
success: true,
code,
message,
data
}
return res.status(code).json(result)
}
exports.checkRequiredExist = (list, required) => {
try {
for (let key of required) {
if (typeof list[key] === 'undefined' || list[key] === null) {
return key
}
}
return false
} catch (error) {
throw error
}
}
exports.setValues = (bucket, keys) => {
let result = { }
try {
for (let key of keys) {
if (typeof bucket[key] !== 'undefined' && bucket[key] !== null) {
result[key] = bucket[key]
}
}
return result
} catch (error) {
throw error
}
}