Skip to content
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

Forget password #134

Merged
merged 19 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,4 @@ jobs:
wget https://raw.githubusercontent.com/arquisoft/wiq_en1b/master/docker-compose.yml -O docker-compose.yml
wget https://raw.githubusercontent.com/arquisoft/wiq_en1b/master/.env
docker compose down
docker compose --profile prod up -d
docker compose --profile prod up -d --pull always
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
depends_on:
- gatewayservice
ports:
- "3000:3000"
- "80:80"

prometheus:
image: prom/prometheus
Expand Down
84 changes: 83 additions & 1 deletion gatewayservice/gateway-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,25 @@ const YAML = require('yaml')
const jwt = require('jsonwebtoken');
const app = express();
const port = 8000;
//Setting up the email
const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: "[email protected]",
pass: "akskfqgakjvcswyg",
},
});

const authServiceUrl = process.env.AUTH_SERVICE_URL || 'http://localhost:8002';
const userServiceUrl = process.env.USER_SERVICE_URL || 'http://localhost:8001';
const questionServiceUrl = process.env.QUESTION_SERVICE_URL || 'http://localhost:8003';
const recordServiceUrl = process.env.RECORD_SERVICE_URL || 'http://localhost:8004';


var forgetPasswords = new Map()

app.use(cors());
app.use(express.json());

Expand All @@ -41,7 +54,50 @@ app.post('/adduser', async (req, res) => {
try {
// Forward the add user request to the user service
const userResponse = await axios.post(userServiceUrl+'/adduser', req.body);
console.log(userResponse)
res.json(userResponse.data);
} catch (error) {
manageError(res, error);

}
});

app.post('/forgetPassword', async (req, res) => {
try {
// Forward the forget password request to the user service
const userResponse = await axios.post(userServiceUrl+'/forgetPassword', req.body);

let sixNumbers = getRandomSixDigitNumber();
while(forgetPasswords.has(sixNumbers))
sixNumbers = getRandomSixDigitNumber();

forgetPasswords.set(sixNumbers, userResponse.data.token)
await sendEmail(res, userResponse.data.email, userResponse.data.username, sixNumbers)
} catch (error) {
manageError(res, error);

}
});

app.get('/tokenFromCode/:code', async (req, res) => {
try {
var code = parseInt(req.params.code);
if(forgetPasswords.has(code)){
var token = forgetPasswords.get(code)
forgetPasswords.delete(code)
res.json({token: token});
}
else
res.status(400).json({ error : "Invalid code" });
} catch (error) {
manageError(res, error);

}
});

app.post('/changePassword', verifyToken, async (req, res) => {
try {
// Forward the forget password request to the user service
const userResponse = await axios.post(userServiceUrl+'/changePassword', req.body);
res.json(userResponse.data);
} catch (error) {
manageError(res, error);
Expand Down Expand Up @@ -235,4 +291,30 @@ function manageError(res, error){
res.status(500).json({error : "Internal server error"})
}

function getRandomSixDigitNumber() {
const now = Date.now(); // Gets the current timestamp
const lastSixDigits = now.toString().slice(-6); // Gets the last 6 digits as a string
return parseInt(lastSixDigits, 10); // Converts it back to an integer
}

async function sendEmail(res, email, username, numbers) {
// Configuración del correo
const mailOptions = {
from: process.env.EMAIL_USER,
to: email,
subject: 'Hello ' + username + ' this is the wiqen1b team',
text: 'We see that you have requested a password change.\n' +
'Please introduce the code: ' + numbers + '. You have around 10 minutes to change your password \n' +
'In case you have not requested a password change forget this email existance',
};

try {
// Envía el correo
await transporter.sendMail(mailOptions);
res.send('Email sent successfully');
} catch (error) {
res.status(500).send('Error sending email');
}
}

module.exports = server
51 changes: 49 additions & 2 deletions gatewayservice/gateway-service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const request = require('supertest');
const axios = require('axios');
const jwt = require('jsonwebtoken');
const app = require('./gateway-service');
const nodemailer = require('nodemailer');

afterAll(async () => {
app.close();
Expand All @@ -12,8 +13,6 @@ jest.mock('jsonwebtoken');

jest.mock('axios');



describe('Gateway Service with mocked micro services', () => {

// Mock responses from external services
Expand All @@ -24,6 +23,10 @@ describe('Gateway Service with mocked micro services', () => {
return Promise.resolve({ data: { username: 'newuser' } });
} else if(url.endsWith('/record')){
return Promise.resolve({data : {user:'testuser'}})
} else if(url.endsWith('/forgetPassword')){
return Promise.resolve({data : { token: 'mockedToken', username : 'testuser', email:"[email protected]"}})
} else if(url.endsWith('/changePassword')){
return Promise.resolve({data : {token: 'mockedToken', username : 'testuser', email:"[email protected]"}})
}
});

Expand Down Expand Up @@ -60,6 +63,14 @@ describe('Gateway Service with mocked micro services', () => {
callback(null, "decoded");
});


//Mock nodemailer
jest.mock('nodemailer', () => ({
createTransport: jest.fn().mockReturnValue({
sendMail: jest.fn(),
}),
}));

// Test /login endpoint
it('should forward login request to auth service', async () => {
const response = await request(app)
Expand Down Expand Up @@ -146,6 +157,42 @@ describe('Gateway Service with mocked micro services', () => {

});

//Test /forgetPassword
it('should forward the request and send an email', async () => {
const response = await request(app)
.post('/forgetPassword')
.send({ email: '[email protected]', username: 'testuser'});
expect(response.statusCode).toBe(200);
expect(response.text).toBe('Email sent successfully');
})

//Test tokenFromCode/:code
it('should find a token', async () => {
//First generate the code:token

const fixedTimestamp = 1683078000000;
jest.spyOn(Date, 'now').mockReturnValue(fixedTimestamp);

await request(app)
.post('/forgetPassword')
.send({ email: '[email protected]', username: 'testuser'});
const response = await request(app).get('/tokenFromCode/000000');

expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty('token', "mockedToken");
})

//Test /changePassword
it('should forward the request', async () => {
const response = await request(app)
.post('/changePassword')
.send({ username: 'testuser', password: 'newpassword' })
.set('token', 'valorDelToken');

expect(response.statusCode).toBe(200);
expect(response.body.username).toBe('testuser');
})

});

function checkRecord(response){
Expand Down
147 changes: 147 additions & 0 deletions gatewayservice/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,153 @@ paths:
type: string
description: Error information.
example: Internal Server Error
/forgetPassword:
post:
summary: Sends a forget password alert to the server
operationId: forgetPassword
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: User email
example: [email protected]
username:
type: string
description: User name
example: student
responses:
'200':
description: Email sent successfully
content:
application/json:
schema:
type: string
example: "Email sent successfully"
'400':
description: Failed to find user
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error information.
example: No user found, review credentials
'500':
description: Internal server error.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error information.
example: Internal Server Error
/tokenFromCode{code}:
get:
summary: Get a token from a 6 digit code
parameters:
- name: code
in: path
required: true
schema:
type: string
responses:
'200':
description: Code found returns token
content:
application/json:
schema:
type: object
properties:
token:
type: string
'400':
description: Invalid code
content:
application/json:
schema:
type: string
example: "Invalid code"
'500':
description: Internal server error.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error information.
example: Internal Server Error
/changePassword:
post:
summary: Changes the password of the authorized user
parameters:
- name: token
in: header
schema:
type: string
operationId: changePassword
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: User email
example: [email protected]
username:
type: string
description: User name
example: student
password:
type: string
description: User password
example: password123
repeatPassword:
type: string
description: Userpassword
example: password123
responses:
'200':
description: Email sent successfully
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'400':
description: Failed to find user
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error information.
example: No user found, review credentials
'500':
description: Internal server error.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error information.
example: Internal Server Error
/login:
post:
summary: Log in to the system.
Expand Down
9 changes: 9 additions & 0 deletions gatewayservice/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion gatewayservice/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
"cors": "^2.8.5",
"express": "^4.18.2",
"express-openapi": "^12.1.3",
"jsonwebtoken": "^9.0.2",
"express-prom-bundle": "^7.0.0",
"jsonwebtoken": "^9.0.2",
"nodemailer": "^6.9.13",
"swagger-ui-express": "^5.0.0",
"yaml": "^2.4.1"
},
Expand Down
Loading