diff --git a/server/app.js b/server/app.js index 932c3ee..45a2156 100644 --- a/server/app.js +++ b/server/app.js @@ -4,7 +4,8 @@ var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); -var apiRouter = require('./routes/index'); +var indexRouter = require('./routes/indexRouter'); +var authRouter = require('./routes/authRouter'); var app = express(); @@ -19,22 +20,12 @@ app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', express.static(path.join(__dirname, '../client/build'))); -app.use('/api', apiRouter); +app.use('/api', indexRouter); +app.use('/api/user', authRouter); // catch 404 and forward to error handler app.use(function (req, res, next) { next(createError(404)); }); -// error handler -app.use(function (err, req, res, next) { - // set locals, only providing error in development - res.locals.message = err.message; - res.locals.error = req.app.get('env') === 'development' ? err : {}; - - // render the error page - res.status(err.status || 500); - res.render('error'); -}); - module.exports = app; diff --git a/server/bin/www b/server/bin/www index 2563a9d..7c46d77 100644 --- a/server/bin/www +++ b/server/bin/www @@ -85,8 +85,10 @@ function onError(error) { async function onListening() { initModels(db); try { - await db.authenticate(); - console.log('Connection has been established successfully.'); + await db.authenticate({ logging: false }); + console.log( + `[INFO]: Database connection to ${process.env.NODE_ENV} has been established successfully.` + ); } catch (error) { console.error('Unable to connect to the database:', error); } diff --git a/server/db.js b/server/db.js index 12999fa..ff2a8fe 100644 --- a/server/db.js +++ b/server/db.js @@ -1,7 +1,7 @@ var { Sequelize, Options } = require('sequelize'); var configs = require('./config/config.js'); -const env = process.env.NODE_ENV || 'development'; +const env = process.env.NODE_ENV; const config = configs[env]; const db = new Sequelize({ diff --git a/server/middleware/basicAuth.js b/server/middleware/basicAuth.js new file mode 100644 index 0000000..9292b94 --- /dev/null +++ b/server/middleware/basicAuth.js @@ -0,0 +1,32 @@ +const forge = require('node-forge'); +const { User } = require('../models/index'); + +async function basicAuth(req, res, next) { + const email = req.body.email; + const password = req.body.password; + + if (!email || !password) { + return res.status(400).send('Request lacking information'); + } + + const result = await User.findOne({ where: { email: email } }); + + if (!result) { + return res.status(400).send('Unauthorized user'); + } + + const hashedPassword = forge.md.sha512 + .create() + .update(password) + .digest() + .toHex(); + + if (hashedPassword === result.password) { + req.user = result; + return next(); + } else { + return res.status(400).send('Unauthorized user'); + } +} + +module.exports = basicAuth; diff --git a/server/middleware/tokenAuth.js b/server/middleware/tokenAuth.js new file mode 100644 index 0000000..e69de29 diff --git a/server/models/Group.js b/server/models/Group.js index b0759df..319d480 100644 --- a/server/models/Group.js +++ b/server/models/Group.js @@ -1,3 +1,4 @@ +const { v4: uuidv4 } = require('uuid'); const { DataTypes, Model } = require('sequelize'); class Group extends Model { @@ -7,6 +8,7 @@ class Group extends Model { groupID: { type: DataTypes.STRING, primaryKey: true, + defaultValue: uuidv4, }, groupName: { type: DataTypes.STRING, diff --git a/server/models/Organization.js b/server/models/Organization.js index 12f416d..e877017 100644 --- a/server/models/Organization.js +++ b/server/models/Organization.js @@ -1,3 +1,4 @@ +const { v4: uuidv4 } = require('uuid'); const { DataTypes, Model } = require('sequelize'); class Organization extends Model { @@ -7,6 +8,7 @@ class Organization extends Model { orgID: { type: DataTypes.STRING, primaryKey: true, + defaultValue: uuidv4, }, orgName: { type: DataTypes.STRING, diff --git a/server/models/Token.js b/server/models/Token.js new file mode 100644 index 0000000..61e6a70 --- /dev/null +++ b/server/models/Token.js @@ -0,0 +1,21 @@ +const { v4: uuidv4 } = require('uuid'); +const { DataTypes, Model } = require('sequelize'); + +class Token extends Model { + static initModel(sequelize) { + Token.init( + { + tokenID: { + type: DataTypes.STRING, + primaryKey: true, + defaultValue: uuidv4, + }, + }, + { sequelize } + ); + + return Token; + } +} + +module.exports = Token; diff --git a/server/models/User.js b/server/models/User.js index a95d54b..40d4668 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,3 +1,4 @@ +const { v4: uuidv4 } = require('uuid'); const { DataTypes, Model } = require('sequelize'); class User extends Model { @@ -7,6 +8,7 @@ class User extends Model { userID: { type: DataTypes.STRING, primaryKey: true, + defaultValue: uuidv4, }, firstName: { type: DataTypes.STRING, @@ -21,21 +23,19 @@ class User extends Model { unique: true, allowNull: false, }, - prounouns: { + password: { + type: DataTypes.STRING, + allowNull: false, + }, + pronouns: { type: DataTypes.STRING, }, gender: { type: DataTypes.STRING, }, - race: { - type: DataTypes.STRING, - }, ethnicity: { type: DataTypes.STRING, }, - createdAt: { - type: DataTypes.DATE, - }, }, { sequelize } ); diff --git a/server/models/index.js b/server/models/index.js index 3c668e7..2fb99ff 100644 --- a/server/models/index.js +++ b/server/models/index.js @@ -1,36 +1,52 @@ const User = require('./User'); const Group = require('./Group'); const Organization = require('./Organization'); +const Token = require('./Token'); -function initModels(sequelize) { +async function initModels(sequelize) { User.initModel(sequelize); Organization.initModel(sequelize); Group.initModel(sequelize); + Token.initModel(sequelize); - User.hasMany(Group, { foreignKey: 'ownerID' }); + User.hasMany(Group, { foreignKey: 'ownerID', onDelete: 'CASCADE' }); Group.belongsTo(User, { foreignKey: 'ownerID' }); - User.hasOne(Organization, { foreignKey: 'ownerID' }); + + User.hasOne(Organization, { foreignKey: 'ownerID', onDelete: 'CASCADE' }); Organization.hasOne(Organization, { foreignKey: 'ownerID' }); - Organization.hasMany(Group, { foreignKey: 'ownerID' }); + + Organization.hasMany(Group, { foreignKey: 'ownerID', onDelete: 'CASCADE' }); + Group.belongsTo(Organization, { foreignKey: 'orgOwnerID' }); - User.belongsToMany(Group, { - through: 'OrganizationUsers', - uniqueKey: 'userID', + + User.hasOne(Token, { foreignKey: 'userID', onDelete: 'CASCADE' }); + Token.belongsTo(User, { foreignKey: 'userID' }); + + User.belongsToMany(Organization, { + through: 'orgUsers', + foreignKey: 'userID', }); Organization.belongsToMany(User, { - through: 'OrganizationUsers', - uniqueKey: 'organizationID', + through: 'orgUsers', + uniqueKey: 'orgID', }); + User.belongsToMany(Group, { - through: 'GroupUsers', - uniqueKey: 'userID', + through: 'groupUsers', + foreignKey: 'userID', }); Group.belongsToMany(User, { - through: 'GroupUsers', - uniqueKey: 'groupID', + through: 'groupUsers', + foreignKey: 'groupID', }); - return { User, Group, Organization }; + if (process.env.NODE_ENV == 'development') { + await sequelize.sync({ logging: false, force: true }); + } else { + await sequelize.sync({ logging: false, alter: true }); + } + + return { User, Group, Organization, Token }; } -module.exports = { User, Organization, Group, initModels }; +module.exports = { User, Organization, Group, Token, initModels }; diff --git a/server/package-lock.json b/server/package-lock.json index d6d0093..a914e22 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,9 +15,11 @@ "jade": "^0.29.0", "morgan": "~1.9.1", "mysql2": "^2.3.3", + "node-forge": "^1.2.1", "nodemon": "^2.0.15", "sequelize": "^6.17.0", - "sqlite3": "^5.0.2" + "sqlite3": "^5.0.2", + "uuid": "^8.3.2" } }, "node_modules/@sindresorhus/is": { @@ -1817,6 +1819,14 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, + "node_modules/node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -4560,6 +4570,11 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, + "node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==" + }, "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", diff --git a/server/package.json b/server/package.json index a3a7155..1c717f8 100644 --- a/server/package.json +++ b/server/package.json @@ -13,8 +13,10 @@ "jade": "^0.29.0", "morgan": "~1.9.1", "mysql2": "^2.3.3", + "node-forge": "^1.2.1", "nodemon": "^2.0.15", "sequelize": "^6.17.0", - "sqlite3": "^5.0.2" + "sqlite3": "^5.0.2", + "uuid": "^8.3.2" } } diff --git a/server/public/stylesheets/style.css b/server/public/stylesheets/style.css deleted file mode 100644 index 9453385..0000000 --- a/server/public/stylesheets/style.css +++ /dev/null @@ -1,8 +0,0 @@ -body { - padding: 50px; - font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; -} - -a { - color: #00B7FF; -} diff --git a/server/routes/auth/CreateUser.js b/server/routes/auth/CreateUser.js new file mode 100644 index 0000000..8b59b8e --- /dev/null +++ b/server/routes/auth/CreateUser.js @@ -0,0 +1,38 @@ +const forge = require('node-forge'); +const { User } = require('../../models/index'); + +async function CreateUser(req, res, next) { + const body = req.body; + + if (!body.firstName || !body.lastName || !body.email || !body.password) { + console.log(body); + return res.status(400).send('The request is missing required fields.'); + } + + const existingUsers = await User.findAll({ + where: { + email: req.body.email, + }, + }); + + if (existingUsers.length !== 0) { + return res.status(500).send('A user with that email already exists.'); + } + + try { + const passwordHash = forge.md.sha512 + .create() + .update(body.password) + .digest() + .toHex(); + const result = await User.create({ ...body, password: passwordHash }); + + return res.send(result); + } catch (e) { + console.log(e); + + return res.status(500).send('Whoops, something went wrong!'); + } +} + +module.exports = CreateUser; diff --git a/server/routes/auth/Login.js b/server/routes/auth/Login.js new file mode 100644 index 0000000..20e57df --- /dev/null +++ b/server/routes/auth/Login.js @@ -0,0 +1,17 @@ +const forge = require('node-forge'); +const { Token } = require('../../models/index'); + +async function Login(req, res, next) { + const token = forge.md.sha512 + .create() + .update(`${new Date()}${req.user.email}`) + .digest() + .toHex(); + + await Token.destroy({ where: { userID: req.user.userID } }); + const newToken = await Token.create({ userID: req.user.userID }); + + res.send(newToken.tokenID); +} + +module.exports = Login; diff --git a/server/routes/authRouter.js b/server/routes/authRouter.js new file mode 100644 index 0000000..6b4ac52 --- /dev/null +++ b/server/routes/authRouter.js @@ -0,0 +1,20 @@ +var express = require('express'); +var router = express.Router(); + +const basicAuth = require('../middleware/basicAuth'); + +const CreateUser = require('./auth/CreateUser'); +const Login = require('./auth/Login'); + +router.post('/register', CreateUser); +router.post('/login', basicAuth, Login); + +/* +User +- Change password +- User Login +- User Profile +- Modify user profile +*/ + +module.exports = router; diff --git a/server/routes/index.js b/server/routes/indexRouter.js similarity index 72% rename from server/routes/index.js rename to server/routes/indexRouter.js index 2df4e8d..54e7b6e 100644 --- a/server/routes/index.js +++ b/server/routes/indexRouter.js @@ -3,17 +3,10 @@ var router = express.Router(); /* GET home page. */ router.get('/', (req, res, next) => { - res.render('index', { title: 'Express' }); + return res.render('index', { title: 'Express' }); }); -/* -User -- Create user -- Change password -- User Login -- User Profile -- Modify user profile - +/* Organization - Create organization - Create organization account diff --git a/server/views/error.jade b/server/views/error.jade deleted file mode 100644 index 51ec12c..0000000 --- a/server/views/error.jade +++ /dev/null @@ -1,6 +0,0 @@ -extends layout - -block content - h1= message - h2= error.status - pre #{error.stack} diff --git a/server/views/index.jade b/server/views/index.jade deleted file mode 100644 index 3d63b9a..0000000 --- a/server/views/index.jade +++ /dev/null @@ -1,5 +0,0 @@ -extends layout - -block content - h1= title - p Welcome to #{title} diff --git a/server/views/layout.jade b/server/views/layout.jade deleted file mode 100644 index 15af079..0000000 --- a/server/views/layout.jade +++ /dev/null @@ -1,7 +0,0 @@ -doctype html -html - head - title= title - link(rel='stylesheet', href='/stylesheets/style.css') - body - block content diff --git a/test_db.sqlite b/test_db.sqlite index e69de29..8712774 100644 Binary files a/test_db.sqlite and b/test_db.sqlite differ