diff --git a/src/config/database.js b/src/config/database.js index 444acb0..8bf7a02 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -2,11 +2,14 @@ const mongoose = require('mongoose'); module.exports = async () => { let BD_URL; - if (process.env.NODE_ENV === "prod") { + if (process.env.NODE_ENV.replace(/'/g, '').trim() === "prod") { + console.log('Conectando com o banco de dados de produção...'); BD_URL = process.env.BD_PROD; - } else if (process.env.NODE_ENV === "dev") { + } else if (process.env.NODE_ENV.replace(/'/g, '').trim() === "dev") { + console.log('Conectando com o banco de dados de desenvolvimento...'); BD_URL = process.env.BD_DEV; } else { + console.log('Gerando banco de dados local...'); const { MongoMemoryServer } = require('mongodb-memory-server'); const mongod = new MongoMemoryServer(); const uri = await mongod.getUri(); @@ -24,6 +27,8 @@ module.exports = async () => { console.log("Erro na conexão com o banco de dados: " + err); }); + mongoose.set("strictQuery", false); + mongoose.connect(BD_URL, { useNewUrlParser: true, // poolSize: 5, diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index c1d2fa1..c7908c7 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -1,49 +1,64 @@ -const jwt = require('jsonwebtoken'); -const User = require('@user/User'); -const { findPresident } = require('@ej/EjService'); +const jwt = require("jsonwebtoken"); +const Member = require("@member/Member"); module.exports = { - authorizeUser(req, res, next) { - authorize(req, res, next, "user"); - }, + validatedUser(req, res, next) { + authorize(req, res, next, "valid"); + }, - authorizePresident(req, res, next) { - authorize(req, res, next, "presidente"); - } -} + authorizedUser(req, res, next) { + authorize(req, res, next, "user"); + }, + + authorizedLeadership(req, res, next) { + authorize(req, res, next, "leadership"); + }, + + authorizePresident(req, res, next) { + next(); + }, +}; const authorize = (req, res, next, type) => { - const authHeader = req.headers.authorization; + const authHeader = req.headers.authorization; - if (!authHeader) - return res.status(401).send({ error: 'Sem token irmão' }); + if (!authHeader) + return res.status(401).send({ error: "Requisição sem token." }); - const parts = authHeader.split(' '); + const parts = authHeader.split(" "); - if (!parts.length === 2) - return res.status(401).send({ error: 'Erro de token' }); + const [scheme, token] = parts.length === 2 ? parts : [null, null]; - const [scheme, token] = parts; + if (scheme === null || !/^Bearer$/i.test(scheme)) + return res.status(401).send({ error: "Token mal formatado." }); - if (!/^Bearer$/i.test(scheme)) - return res.status(401).send({ error: 'Token mal formatado' }); + jwt.verify(token, process.env.JWT_SECRET, async (err, decoded) => { + if (err) return res.status(401).send({ error: "Token inválido." }); - jwt.verify(token, process.env.JWT_SECRET, async (err, decoded) => { - if (err) - return res.status(401).send({ error: 'Token inválido' }); + const member = await Member.findOne({ _id: decoded.sub }); - const user = await User.findOne({ _id: decoded.sub }); + switch (type) { + case "valid": + if (!member) + return res.status(404).send({ error: "Usuário não existe." }); + break; - if (!user) - return res.status(404).send({ error: 'Usuário não existe!' }); + case "user": + if (!isLeadership(member) && member._id.toString() !== req.body._id) + return res.status(403).send({ error: "Usuário sem permissão." }); + break; - const president = await findPresident(user.ej); + case "leadership": + if (!isLeadership(member)) + return res.status(403).send({ error: "Usuário sem permissão." }); + break; + } - if (type === "presidente" && `${user._id}` !== `${president._id}`) - return res.status(403).send({ error: 'Usuário não permitido por aqui!' }); + req.ejId = member.ej; + req.memberId = member._id; + return next(); + }); +}; - req.ejId = user.ej; - req.userId = user._id; - return next(); - }) -} \ No newline at end of file +const isLeadership = (member) => + ["Presidente", "Diretor(a)"].includes(`${member.role}`); diff --git a/src/modules/Ej/EjService.js b/src/modules/Ej/EjService.js index 7790fdc..24b0ff4 100644 --- a/src/modules/Ej/EjService.js +++ b/src/modules/Ej/EjService.js @@ -1,5 +1,5 @@ const Ej = require('@ej/Ej'); -const User = require('@user/User'); +const Member = require('@member/Member'); const bcrypt = require('bcrypt'); module.exports = { @@ -7,12 +7,19 @@ module.exports = { const { name } = ejData; const { presidentData } = ejData; + const member = await Member.findOne({ email: presidentData.email }); + if (member) { + throw new Error('Já existe uma EJ cadastrada para esse email!'); + } + const ej = await Ej.create({ name: name }) const psw = await bcrypt.hash(presidentData.password, parseInt(process.env.SALT_ROUNDS)) - const user = await User.create({ + + const newMember = await Member.create({ + name: presidentData.name, email: presidentData.email, birthDate: presidentData.birthDate, @@ -21,8 +28,8 @@ module.exports = { ej: ej._id }) - user.password = undefined - return { ej: ej, user: user } + newMember.password = undefined + return { ej: ej, member: newMember } }, // only for test purposes @@ -34,12 +41,12 @@ module.exports = { }, async findPresident(ejId) { - const president = await User.findOne({ role: 'presidente', ej: ejId }); + const president = await Member.findOne({ role: 'presidente', ej: ejId }); return president; }, async findById(ejId) { const ej = await Ej.findOne({ _id: ejId }); - return ej - } + return ej; + }, } \ No newline at end of file diff --git a/src/modules/Link/LinkRoutes.js b/src/modules/Link/LinkRoutes.js index c0d924a..0a63f5a 100644 --- a/src/modules/Link/LinkRoutes.js +++ b/src/modules/Link/LinkRoutes.js @@ -1,10 +1,14 @@ -const router = require('express').Router(); -const { save, findByEj, update, remove } = require('./LinkController'); -const { authorizeUser } = require('@middlewares/auth') +const router = require("express").Router(); +const { save, findByEj, update, remove } = require("./LinkController"); +const { + validatedUser, + authorizedUser, + authorizedLeadership, +} = require("@middlewares/auth"); -router.post('/link', authorizeUser, save); -router.get('/link', authorizeUser, findByEj); -router.patch('/link/:id', authorizeUser, update); -router.delete('/link/:id', authorizeUser, remove); +router.post("/link", authorizedLeadership, save); +router.get("/link", validatedUser, findByEj); +router.patch("/link/:id", authorizedUser, update); +router.delete("/link/:id", authorizedLeadership, remove); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/modules/Member/Auth/AuthController.js b/src/modules/Member/Auth/AuthController.js new file mode 100644 index 0000000..ecb98dc --- /dev/null +++ b/src/modules/Member/Auth/AuthController.js @@ -0,0 +1,16 @@ +const { signIn } = require('./AuthService'); + +module.exports = { + async signIn(req, res) { + try { + const dados = await signIn(req.body) + + if (dados.hasOwnProperty('erro')) { + return res.status(401).send({ error: dados.erro }) + } + return res.status(200).send({ dados: dados }) + } catch (error) { + return res.status(500).send({ error: error.message }) + } + }, +} \ No newline at end of file diff --git a/src/modules/Member/Auth/AuthRoutes.js b/src/modules/Member/Auth/AuthRoutes.js new file mode 100644 index 0000000..becebea --- /dev/null +++ b/src/modules/Member/Auth/AuthRoutes.js @@ -0,0 +1,6 @@ +const router = require('express').Router(); +const { signIn } = require('./AuthController'); + +router.post('/signIn', signIn); + +module.exports = router; \ No newline at end of file diff --git a/src/modules/Member/Auth/AuthService.js b/src/modules/Member/Auth/AuthService.js new file mode 100644 index 0000000..ce0a0c3 --- /dev/null +++ b/src/modules/Member/Auth/AuthService.js @@ -0,0 +1,40 @@ +const bcrypt = require('bcrypt'); +const Member = require('@member/Member'); +const JWT = require('jsonwebtoken'); + +const signToken = member => { + return JWT.sign({ + iss: 'TCC', + sub: member, + iat: new Date().getTime(), + }, process.env.JWT_SECRET); +} + +module.exports = { + async signIn(dados) { + const { email, password } = dados; + + // ao recuperar o objeto 'member', ele conterá o campo 'ej' preenchido com o nome da ej + const member = await Member.findOne({ email: email }).populate({ path: 'ej', select: 'name' }); + + if (!member) + return { erro: 'Usuário ou senha incorreta' } + + const match = await bcrypt.compare(password, member.password); + + if (!match) + return { erro: 'Usuário ou senha incorreta' } + + delete member._doc.password + + // remove o campo password de 'member' antes de retorná-lo + const token = signToken(member); + + + // return res.status(200).cookie("jwt", token, {sameSite: 'none', path: '/', httpOnly: false , secure: false }).send({ + return { + "member": member, + "token": token // retirar isso em produção + }; + }, +} \ No newline at end of file diff --git a/src/modules/Member/Member.js b/src/modules/Member/Member.js index 9b498f9..3f020a8 100644 --- a/src/modules/Member/Member.js +++ b/src/modules/Member/Member.js @@ -12,10 +12,14 @@ const MemberSchema = new Schema({ }, role: { type: String, - enum: ['assessor', 'diretor', 'trainee', 'estagiário', 'membro'], + enum: ["Presidente", "Diretor(a)", "Assessor(a)", "Conselheiro(a)"], required: true, - default: 'membro' - }, + default: "Assessor(a)", + }, + password: { + type: String, + required: true, + }, ej: { type: Schema.Types.ObjectId, ref: Ej, @@ -27,7 +31,7 @@ const MemberSchema = new Schema({ }, entryDate: { type: Date, - required: true + required: false }, phone: { type: String, diff --git a/src/modules/Member/MemberRoutes.js b/src/modules/Member/MemberRoutes.js index 635d651..f55012a 100644 --- a/src/modules/Member/MemberRoutes.js +++ b/src/modules/Member/MemberRoutes.js @@ -1,10 +1,14 @@ -const router = require('express').Router(); -const { save, findByEj, update, remove } = require('./MemberController'); -const { authorizeUser } = require('@middlewares/auth') +const router = require("express").Router(); +const { save, findByEj, update, remove } = require("./MemberController"); +const { + validatedUser, + authorizedUser, + authorizedLeadership, +} = require("@middlewares/auth"); -router.post('/member', authorizeUser, save); -router.get('/member', authorizeUser, findByEj); -router.patch('/member/:id', authorizeUser, update); -router.delete('/member/:id', authorizeUser, remove); +router.post("/member", authorizedLeadership, save); +router.get("/member", validatedUser, findByEj); +router.patch("/member/:id", authorizedUser, update); +router.delete("/member/:id", authorizedLeadership, remove); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/modules/Member/MemberService.js b/src/modules/Member/MemberService.js index 2c4eceb..b2b75a0 100644 --- a/src/modules/Member/MemberService.js +++ b/src/modules/Member/MemberService.js @@ -1,41 +1,91 @@ -const Ej = require('@ej/Ej'); -const Member = require('@member/Member'); +const bcrypt = require("bcrypt"); +const Ej = require("@ej/Ej"); +const Member = require("@member/Member"); module.exports = { - async save(memberData, ejId) { - const { name, email, role, birthDate, entryDate, phone, observations, habilities, department } = memberData; - - const member = await Member.create({ - name, - email, - role, - birthDate, - ej: ejId, - entryDate, - phone, - observations, - habilities, - department - }) - - return member; - }, - - // only for test purposes - async findByEj(ejId) { - // const ejs = await Ej.find().populate({ path: 'president', select: 'name -_id' }); - const members = await Member.find({ ej: ejId }); - - return members; - }, - - async remove(memberId) { - const member = await Member.deleteOne({ _id: memberId }); - return member; - }, - - async update(memberId, data) { - const updatedMember = await Member.findOneAndUpdate({ _id: memberId }, data) - return updatedMember - } -} \ No newline at end of file + async save(memberData, ejId) { + const { + name, + email, + role, + password, + birthDate, + entryDate, + phone, + observations, + habilities, + department, + } = memberData; + + const psw = await bcrypt.hash( + `${password}`, + parseInt(process.env.SALT_ROUNDS) + ); + + const member = await Member.create({ + name, + email, + role, + password, + birthDate, + ej: ejId, + entryDate, + phone, + observations, + habilities, + department, + }); + + return getDTOmember(member); + }, + + // only for test purposes + async findByEj(ejId) { + // const ejs = await Ej.find().populate({ path: 'president', select: 'name -_id' }); + const members = await Member.find({ ej: ejId }); + + const membersDTO = members.map((member) => { + return getDTOmember(member); + }); + + return membersDTO; + }, + + async remove(memberId) { + const member = await Member.deleteOne({ _id: memberId }); + return member; + }, + + async update(memberId, data) { + if (data.hasOwnProperty("password")) { + const psw = await bcrypt.hash( + data.password, + parseInt(process.env.SALT_ROUNDS) + ); + data.password = psw; + } + + const updatedMember = await Member.findOneAndUpdate( + { _id: memberId }, + data + ); + + return getDTOmember(updatedMember); + }, +}; + +function getDTOmember(member) { + return { + _id: member._id, + name: member.name, + email: member.email, + role: member.role, + ej: member.ej, + birthDate: member.birthDate, + entryDate: member.entryDate, + phone: member.phone, + observations: member.observations, + habilities: member.habilities, + department: member.department, + }; +} diff --git a/src/modules/Project/ProjectRoutes.js b/src/modules/Project/ProjectRoutes.js index 4caa79d..9cd99ac 100644 --- a/src/modules/Project/ProjectRoutes.js +++ b/src/modules/Project/ProjectRoutes.js @@ -1,10 +1,14 @@ -const router = require('express').Router(); -const { save, findByEj, update, remove } = require('./ProjectController'); -const { authorizeUser } = require('@middlewares/auth') +const router = require("express").Router(); +const { save, findByEj, update, remove } = require("./ProjectController"); +const { + validatedUser, + authorizedUser, + authorizedLeadership, +} = require("@middlewares/auth"); -router.post('/project', authorizeUser, save); -router.get('/project', authorizeUser, findByEj); -router.patch('/project/:id', authorizeUser, update); -router.delete('/project/:id', authorizeUser, remove); +router.post("/project", authorizedLeadership, save); +router.get("/project", validatedUser, findByEj); +router.patch("/project/:id", authorizedUser, update); +router.delete("/project/:id", authorizedLeadership, remove); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/modules/User/User.js b/src/modules/User/User.js index 60d4554..3f880ad 100644 --- a/src/modules/User/User.js +++ b/src/modules/User/User.js @@ -17,7 +17,7 @@ const UserSchema = new Schema( }, role: { type: String, - enum: ["Presidente", "Diretor(a)", "Assessor(a)", "Conselheiro(a)"], + enum: ["Presidente", "Diretor(a)", "Assessor(a)", "Conselheiro(a)","Pós-Júnior", "Guardiã(o)", "Trainee", "Ex-Trainee"], required: true, default: "Assessor(a)", }, diff --git a/src/modules/User/UserRoutes.js b/src/modules/User/UserRoutes.js index 315ca3a..879bec3 100644 --- a/src/modules/User/UserRoutes.js +++ b/src/modules/User/UserRoutes.js @@ -1,10 +1,15 @@ -const router = require('express').Router(); -const { save, findByEj, remove, update } = require('./UserController'); -const { authorizePresident, authorizeUser } = require('@middlewares/auth') +const router = require("express").Router(); +const { save, findByEj, remove, update } = require("./UserController"); +const { + validatedUser, + authorizedUser, + authorizedLeadership, + authorizePresident, +} = require("@middlewares/auth"); -router.post('/user', authorizePresident, save); -router.get('/user', authorizePresident, findByEj); -router.delete('/user/:id', authorizePresident, remove); -router.patch('/user/:id', authorizeUser, update); +router.post("/user", authorizedLeadership, save); +router.get("/user", validatedUser, findByEj); +router.patch("/user/:id", authorizedUser, update); +router.delete("/user/:id", authorizedLeadership, remove); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/modules/User/UserService.js b/src/modules/User/UserService.js index f83a814..8c5857e 100644 --- a/src/modules/User/UserService.js +++ b/src/modules/User/UserService.js @@ -5,12 +5,17 @@ const { remove } = require('../Ej/Ej'); module.exports = { async save(userData, ejId) { const { name, email, role, birthDate, password } = userData + + const user = await User.findOne({ email: userData.email }); + if (user) { + throw new Error('Já existe um usuário cadastrado para esse email!'); + } // password auto generated for first access with 6 digits, mockado por enquanto const code = 123456 || Math.floor(Math.random() * (999999 - 100000) + 100000); const psw = await bcrypt.hash(`${password || code}`, parseInt(process.env.SALT_ROUNDS)) - const user = await User.create({ + const newUser = await User.create({ name: name, email: email, birthDate: birthDate, @@ -20,7 +25,7 @@ module.exports = { }) userData.senhaGerada = password || code - userData._id = user._id + userData._id = newUser._id return userData; }, diff --git a/src/server.js b/src/server.js index 4046f93..7bc116b 100644 --- a/src/server.js +++ b/src/server.js @@ -7,7 +7,7 @@ require('dotenv/config'); const EjRoutes = require('./modules/Ej/EjRoutes'); const UserRoutes = require('./modules/User/UserRoutes'); -const AuthRoutes = require('./modules/User/Auth/AuthRoutes'); +const AuthRoutes = require('./modules/Member/Auth/AuthRoutes'); const MemberRoutes = require('./modules/Member/MemberRoutes'); const ProjectRoutes = require('./modules/Project/ProjectRoutes'); const LinkRoutes = require('./modules/Link/LinkRoutes');