-
Notifications
You must be signed in to change notification settings - Fork 347
solution #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
solution #272
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { Sequelize } from 'sequelize'; | ||
| import dotenv from 'dotenv'; | ||
|
|
||
| dotenv.config(); | ||
|
|
||
| const sequelize = new Sequelize( | ||
| process.env.DB_NAME, | ||
| process.env.DB_USER, | ||
| process.env.DB_PASSWORD, | ||
| { | ||
| host: process.env.DB_HOST, | ||
| dialect: 'postgres', | ||
| }, | ||
| ); | ||
|
|
||
| export default sequelize; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import userService from '../services/user.service.js'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires the profile page to allow changing name. The |
||
|
|
||
| class UserController { | ||
| async registration(req, res) { | ||
| try { | ||
| const { name, email, password } = req.body; | ||
| const userData = await userService.registration(name, email, password); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async login(req, res) { | ||
| try { | ||
| const { email, password } = req.body; | ||
| const userData = await userService.login(email, password); | ||
|
|
||
| res.cookie('token', userData.token, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| sameSite: 'lax', | ||
| }); | ||
|
|
||
| return res.json(userData); | ||
| } catch (e) { | ||
| res.status(401).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async activate(req, res) { | ||
| try { | ||
| const { token } = req.params; | ||
| const jwtToken = await userService.activate(token); | ||
|
|
||
| res.cookie('token', jwtToken, { | ||
| maxAge: 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
|
|
||
| return res.redirect(`${process.env.CLIENT_URL}/profile?activated=true`); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
|
Comment on lines
+41
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing confirmation field validation for password update - the task requires: new password AND confirmation (must equal new password). Currently only validates oldPassword and newPassword. Add: |
||
| } | ||
|
|
||
| async logout(req, res) { | ||
| try { | ||
| res.clearCookie('token'); | ||
|
Comment on lines
+1
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing route for name update - the
Comment on lines
+1
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing forgotPassword route - the
Comment on lines
+1
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing resetPassword route - the |
||
|
|
||
| return res.json({ message: 'Вышли из системы' }); | ||
| } catch (e) { | ||
| res.status(500).json({ message: 'Ошибка логаута' }); | ||
| } | ||
| } | ||
|
|
||
| async getProfile(req, res) { | ||
| try { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The controller calls |
||
| const user = await userService.getProfile(req.user.userId); | ||
|
|
||
| return res.json(user); | ||
| } catch (e) { | ||
| res.status(500).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing confirmation field validation for email update - the task requires: password, new email, AND confirm email (confirmation must equal new email). Currently only validates password and newEmail. Add: |
||
| async updatePassword(req, res) { | ||
| try { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires password change with fields: old password, |
||
| const { oldPassword, newPassword } = req.body; | ||
|
|
||
| await userService.updatePassword( | ||
| req.user.userId, | ||
| oldPassword, | ||
| newPassword, | ||
| ); | ||
|
|
||
| return res.json({ message: 'Пароль успешно обновлен' }); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
|
|
||
| async updateEmail(req, res) { | ||
| try { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires email change with: type password, |
||
| const { password, newEmail } = req.body; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| await userService.updateEmail(req.user.userId, password, newEmail); | ||
|
|
||
| return res.json({ message: 'Инструкции отправлены на почту' }); | ||
| } catch (e) { | ||
| res.status(400).json({ message: e.message }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default new UserController(); | ||
|
Comment on lines
+1
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing updateName controller method -
Comment on lines
+1
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing forgotPassword controller method -
Comment on lines
+1
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing resetPassword controller method - |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,41 @@ | ||
| 'use strict'; | ||
|
|
||
| import express from 'express'; | ||
| import cookieParser from 'cookie-parser'; | ||
| import cors from 'cors'; | ||
|
|
||
| import router from './router/index.js'; | ||
|
|
||
| import sequelize from './config/db.js'; | ||
|
|
||
| const app = express(); | ||
|
|
||
| app.use(express.json()); | ||
| app.use(cookieParser()); | ||
|
|
||
| app.use( | ||
| cors({ | ||
| credentials: true, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability. Using template literals with
Comment on lines
+17
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability: |
||
| origin: true, | ||
| }), | ||
| ); | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability. Using template literals with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability: |
||
| app.use('/api', router); | ||
|
|
||
| app.use((req, res) => { | ||
| res.status(404).json({ message: 'Page Not Found' }); | ||
| }); | ||
|
|
||
| const start = async () => { | ||
| try { | ||
| await sequelize.authenticate(); | ||
| await sequelize.sync(); | ||
| // eslint-disable-next-line no-console | ||
| app.listen(process.env.PORT || 5000, () => console.log('Server started')); | ||
| } catch (e) { | ||
| // eslint-disable-next-line no-console | ||
| console.log(e); | ||
| } | ||
| }; | ||
|
|
||
| start(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import pg from 'pg'; | ||
| import dotenv from 'dotenv'; | ||
|
|
||
| dotenv.config(); | ||
|
|
||
| const createDatabase = async () => { | ||
| const client = new pg.Client({ | ||
| host: process.env.DB_HOST, | ||
| port: process.env.DB_PORT, | ||
| user: process.env.DB_USER, | ||
| password: process.env.DB_PASSWORD, | ||
| database: 'postgres', | ||
| }); | ||
|
|
||
| try { | ||
| await client.connect(); | ||
|
|
||
| const res = await client.query( | ||
| `SELECT 1 FROM pg_database WHERE datname = '${process.env.DB_NAME}'`, | ||
| ); | ||
|
|
||
| if (res.rowCount === 0) { | ||
| await client.query(`CREATE DATABASE ${process.env.DB_NAME}`); | ||
| /* eslint-disable no-console */ | ||
| console.log(` База данных ${process.env.DB_NAME} создана!`); | ||
| } else { | ||
| console.log('ℹБаза данных уже существует.'); | ||
| } | ||
| } catch (err) { | ||
| console.error('Ошибка при создании базы:', err); | ||
| } finally { | ||
| await client.end(); | ||
| } | ||
| }; | ||
|
|
||
| createDatabase(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import tokenService from '../services/token.service.js'; | ||
|
|
||
| export default function (req, res, next) { | ||
| try { | ||
| const token = req.cookies.token; | ||
|
|
||
| if (!token) { | ||
| return res.status(401).json({ message: 'Пользователь не авторизован' }); | ||
| } | ||
|
|
||
| const userData = tokenService.validateToken(token); | ||
|
|
||
| if (!userData) { | ||
| return res.status(401).json({ message: 'Невалидный токен' }); | ||
| } | ||
|
|
||
| req.user = userData; | ||
| next(); | ||
| } catch (e) { | ||
| return res.status(401).json({ message: 'Пользователь не авторизован' }); | ||
| } | ||
| } | ||
|
Comment on lines
+18
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability - Using template literals with |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { validationResult } from 'express-validator'; | ||
|
|
||
| export default function (req, res, next) { | ||
| const errors = validationResult(req); | ||
|
|
||
| if (!errors.isEmpty()) { | ||
| return res.status(400).json({ | ||
| message: 'Ошибка при валидации', | ||
| errors: errors.array(), | ||
| }); | ||
| } | ||
| next(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import sequelize from '../config/db.js'; | ||
|
|
||
| const User = sequelize.define( | ||
| 'User', | ||
| { | ||
| name: { type: DataTypes.STRING, allowNull: false }, | ||
| email: { type: DataTypes.STRING, unique: true, allowNull: false }, | ||
| password: { type: DataTypes.STRING, allowNull: false }, | ||
| isActive: { type: DataTypes.BOOLEAN, defaultValue: false }, | ||
| activationToken: { type: DataTypes.STRING }, | ||
| resetToken: { type: DataTypes.STRING }, | ||
|
|
||
| pendingEmail: { type: DataTypes.STRING }, | ||
| }, | ||
| { timestamps: true }, | ||
| ); | ||
|
|
||
| export default User; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { Router } from 'express'; | ||
| import { body } from 'express-validator'; | ||
| import userController from '../controllers/user.controller.js'; | ||
| import authMiddleware from '../middleware/auth.middleware.js'; | ||
| import validationMiddleware from '../middleware/validation.middleware.js'; | ||
|
|
||
| const router = new Router(); | ||
|
|
||
| router.post( | ||
| '/registration', | ||
| body('email').isEmail().withMessage('Некорректный email'), | ||
| body('password') | ||
| .isLength({ min: 8 }) | ||
| .withMessage('Пароль должен быть не менее 8 символов') | ||
| .matches(/\d/) | ||
| .withMessage('Пароль должен содержать хотя бы одну цифру') | ||
| .matches(/[A-Z]/) | ||
| .withMessage('Пароль должен содержать заглавную букву'), | ||
| body('name').notEmpty().withMessage('Имя не может быть пустым'), | ||
| validationMiddleware, | ||
| userController.registration, | ||
| ); | ||
|
Comment on lines
+19
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability - |
||
|
|
||
| router.get('/activate/:token', userController.activate); | ||
|
|
||
| router.post( | ||
| '/login', | ||
| body('email').isEmail().withMessage('Некорректный email'), | ||
| body('password').notEmpty().withMessage('Введите пароль'), | ||
| validationMiddleware, | ||
| userController.login, | ||
| ); | ||
|
|
||
| router.get('/profile', authMiddleware, userController.getProfile); | ||
| router.post('/logout', authMiddleware, userController.logout); | ||
|
|
||
| router.put( | ||
| '/update-password', | ||
| authMiddleware, | ||
| body('oldPassword').notEmpty().withMessage('Введите старый пароль'), | ||
| body('newPassword') | ||
| .isLength({ min: 8 }) | ||
| .withMessage('Новый пароль от 8 символов'), | ||
| validationMiddleware, | ||
| userController.updatePassword, | ||
|
Comment on lines
+70
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires password change with 'old password, new password and confirmation'. The route only accepts
Comment on lines
+70
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing confirmation field validation for password update. The task requires: 'require an old one, |
||
| ); | ||
|
|
||
| router.put( | ||
| '/update-email', | ||
| authMiddleware, | ||
| body('newEmail').isEmail().withMessage('Введите корректный новый email'), | ||
| body('password').notEmpty().withMessage('Для подтверждения нужен пароль'), | ||
| validationMiddleware, | ||
| userController.updateEmail, | ||
|
Comment on lines
+88
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires email change with 'type password, confirm the new email, notify old email about the change'. The route only accepts
Comment on lines
+1
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing PUT
Comment on lines
+1
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing POST
Comment on lines
+1
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing POST
Comment on lines
+88
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing confirmEmail field validation for email update. The task requires: 'type the password, confirm the new email'. Add |
||
| ); | ||
|
|
||
|
Comment on lines
+32
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires profile page to allow changing name ('You can change a name'), but there's no
Comment on lines
+32
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires password reset functionality: 'Ask for an email' → 'Show email sent page' → 'Reset Password confirmation page (with password and confirmation fields that must be equal)' → 'Show Success page with link to login'. None of these endpoints exist - missing |
||
| export default router; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import nodemailer from 'nodemailer'; | ||
|
|
||
| class MailService { | ||
| constructor() { | ||
| this.transporter = nodemailer.createTransport({ | ||
| host: process.env.SMTP_HOST, | ||
| port: process.env.SMTP_PORT, | ||
| secure: false, | ||
| auth: { | ||
| user: process.env.SMTP_USER, | ||
| pass: process.env.SMTP_PASSWORD, | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| async sendActivationMail(to, link) { | ||
| await this.transporter.sendMail({ | ||
| from: process.env.SMTP_USER, | ||
| to, | ||
| subject: 'Активация аккаунта', | ||
| text: '', | ||
| html: `<div><h1>Для активации перейдите по ссылке</h1><a href="${link}">${link}</a></div>`, | ||
|
Comment on lines
+18
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL injection vulnerability: Using template literals with |
||
| }); | ||
| } | ||
|
|
||
| async sendNotification(to, subject, text) { | ||
| await this.transporter.sendMail({ | ||
| from: process.env.SMTP_USER, | ||
| to, | ||
| subject, | ||
| text, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| export default new MailService(); | ||
|
Comment on lines
+1
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import jwt from 'jsonwebtoken'; | ||
|
|
||
| class TokenService { | ||
| generateToken(payload) { | ||
| return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '24h' }); | ||
| } | ||
|
|
||
| validateToken(token) { | ||
| try { | ||
| return jwt.verify(token, process.env.JWT_SECRET); | ||
| } catch (e) { | ||
| return null; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default new TokenService(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The task requires password reset functionality with: 1) Ask for email, 2) Show email sent page, 3) Reset password confirmation page with
passwordandconfirmationfields that must be equal, 4) Show success page with link to login. None of these endpoints exist - missing/forgot-passwordand/reset-password/:tokenroutes.