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

Finished user managment #92

Merged
merged 23 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1973385
Added cookie managment in the whole application
Mister-Mario Apr 14, 2024
a65f6e5
Forgot to add the token verifier on some endpoints
Mister-Mario Apr 14, 2024
e9ad477
Added token to the test requests of the gateway service
Mister-Mario Apr 14, 2024
cbb7d35
Mocked jwt in gateway tests
Mister-Mario Apr 14, 2024
32b7a62
Added test to check no token requests
Mister-Mario Apr 14, 2024
dd1c1fe
Removed unused import
Mister-Mario Apr 14, 2024
24931ad
Added logout functionality
Mister-Mario Apr 14, 2024
24f10df
Added restrictions to routes in the webapp
Mister-Mario Apr 14, 2024
490c2ba
Added a reload when logging out too
Mister-Mario Apr 14, 2024
62d7d76
Added new test case for code cover
Mister-Mario Apr 14, 2024
46ca537
Added email to users at the backend
Mister-Mario Apr 14, 2024
257db61
Removed weird import prob vs code stuff
Mister-Mario Apr 14, 2024
57b44f3
Added changes to fix code duplication issues and security flaws
Mister-Mario Apr 14, 2024
22dba52
Try to reduce duplicate code
Mister-Mario Apr 14, 2024
bb17d39
Added email field on login and register and be able to login with use…
Mister-Mario Apr 15, 2024
72850e7
Merge branch 'master' into 88-users-enhancenment
Mister-Mario Apr 21, 2024
51ff839
Fixed all tests that did not use the Cookie and the email
Mister-Mario Apr 21, 2024
de0e44d
Now registering is the same as login, added e2e tests
Mister-Mario Apr 22, 2024
7d79eb4
Made npm install to sync package.json and package-lock.json
Mister-Mario Apr 22, 2024
975cca0
Forgot to remove headless mode
Mister-Mario Apr 22, 2024
19a68d3
Now e2e should work
Mister-Mario Apr 22, 2024
e27f842
Added validations to solve sonarcloud security ranking
Mister-Mario Apr 22, 2024
1b05925
Fixed addUser test failing cause the new endpoint
Mister-Mario Apr 22, 2024
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: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ jobs:
- uses: actions/checkout@v4
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@v5
env:
JWT_KEY: ${{secrets.JWT_KEY}}
with:
name: arquisoft/wiq_en1b/authservice
username: ${{ github.actor }}
Expand Down
32 changes: 26 additions & 6 deletions gatewayservice/gateway-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const promBundle = require('express-prom-bundle');
const swaggerUi = require('swagger-ui-express');
const fs = require("fs")
const YAML = require('yaml')

const jwt = require('jsonwebtoken');
const app = express();
const port = 8000;

Expand Down Expand Up @@ -47,7 +47,7 @@ app.post('/adduser', async (req, res) => {
}
});

app.get('/questions', async (req, res) => {
app.get('/questions', verifyToken, async (req, res) => {
try {

// Forward the question request to the quetion service
Expand All @@ -58,11 +58,11 @@ app.get('/questions', async (req, res) => {
}
});

app.get('/questions/:lang', async (req, res) => {
app.get('/questions/:lang', verifyToken, async (req, res) => {
try {
const lang = req.params.lang;
// Forward the question request to the quetion service
const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang);
const questionResponse = await axios.get(questionServiceUrl+'/questions/' + lang.toString());

Check warning

Code scanning / SonarCloud

Server-side requests should not be vulnerable to forging attacks Medium

Change this code to not construct the URL from user-controlled data. See more on SonarCloud

res.json(questionResponse.data);
} catch (error) {
Expand All @@ -71,7 +71,8 @@ app.get('/questions/:lang', async (req, res) => {
}
});

app.post('/record', async(req, res) => {
app.post('/record', verifyToken, async(req, res) => {
console.log("in")
try {
// Forward the record request to the record service
const recordResponse = await axios.post(recordServiceUrl+'/record', req.body);
Expand All @@ -81,7 +82,7 @@ app.post('/record', async(req, res) => {
}
});

app.get('/record/:user', async(req, res)=>{
app.get('/record/:user', verifyToken, async(req, res)=>{
try {
const user = req.params.user;
// Forward the record request to the record service
Expand All @@ -108,4 +109,23 @@ const server = app.listen(port, () => {
console.log(`Gateway Service listening at http://localhost:${port}`);
});

function verifyToken(req, res, next) {
// Get the token from the request headers
const token = req.headers['token'] || req.body.token || req.query.token;

// Verify if the token is valid
jwt.verify(token, (process.env.JWT_KEY??'my-key'), (err, decoded) => {
if (err) {
// Token is not valid
res.status(403).json({authorized: false,
error: 'Invalid token or outdated'});
} else {
// Token is valid
req.decodedToken = decoded;
// Call next() to proceed to the next middleware or route handler
next();
}
});
}

module.exports = server
40 changes: 34 additions & 6 deletions gatewayservice/gateway-service.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
const request = require('supertest');
const axios = require('axios');
const jwt = require('jsonwebtoken');
const app = require('./gateway-service');

afterAll(async () => {
app.close();
});


jest.mock('jsonwebtoken');

jest.mock('axios');

describe('Gateway Service', () => {


describe('Gateway Service with token mock', () => {

// Mock responses from external services
axios.post.mockImplementation((url, data) => {
if (url.endsWith('/login')) {
Expand All @@ -33,6 +40,14 @@ describe('Gateway Service', () => {
}
});



// Mock the `verify` function of JWT
jwt.verify.mockImplementation((token, secretOrPublicKey, callback) => {
// Assume the token is valid and return the payload
callback(null, "decoded");
});

// Test /login endpoint
it('should forward login request to auth service', async () => {
const response = await request(app)
Expand All @@ -57,7 +72,7 @@ describe('Gateway Service', () => {
// Test /questions endpoint
it('should forward questions request to question service', async () => {
const response = await request(app)
.get('/questions');
.get('/questions').set('token', 'valorDelToken');

expect(response.statusCode).toBe(200);
expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?");
Expand All @@ -66,7 +81,7 @@ describe('Gateway Service', () => {
// Test /questions/:lang endpoint
it('should forward questions request to question service', async () => {
const response = await request(app)
.get('/questions/es');
.get('/questions/es').set('token', 'valorDelToken');

expect(response.statusCode).toBe(200);
expect(response.body[0]).toHaveProperty('question', "¿Cuál es la población de Oviedo?");
Expand All @@ -75,7 +90,7 @@ describe('Gateway Service', () => {
// Test /record endpoint
it('should forward record request to record service', async () => {
const response = await request(app)
.post('/record');
.post('/record').set('token', 'valorDelToken');

expect(response.statusCode).toBe(200);
expect(response.body.user).toBe('testuser');
Expand All @@ -84,9 +99,22 @@ describe('Gateway Service', () => {
// Test /record/:user endpoint
it('should forward record request to record service', async () => {
const response = await request(app)
.get('/record/testuser');
.get('/record/testuser').set('token', 'valorDelToken');
console.log(response)
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty('record', "undefined");
});


});

describe('Gateway Service without token mock', () => {
// Test /record/:user endpoint
it('should not verify the token', async () => {
const response = await request(app)
.get('/record/testuser');
console.log(response)
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty('record', "undefined");
});
});
})
124 changes: 124 additions & 0 deletions gatewayservice/package-lock.json

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

1 change: 1 addition & 0 deletions gatewayservice/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"cors": "^2.8.5",
"express": "^4.18.2",
"express-openapi": "^12.1.3",
"jsonwebtoken": "^9.0.2",
"express-prom-bundle": "^7.0.0",
"swagger-ui-express": "^5.0.0",
"yaml": "^2.4.1"
Expand Down
2 changes: 1 addition & 1 deletion questionservice/question-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ app.get('/questions/:lang', async (req, res) => {
const lang = req.params.lang;

const questions = await Question.aggregate([
{$match: {language : lang}}, //Condition
{$match: {language : lang.toString()}}, //Condition
{$sample: {size:5}} //5 random from the ones that fullfil the condition
]);

Expand Down
1 change: 1 addition & 0 deletions users/authservice/auth-model.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
email: String,
username: String,
password: String,
createdAt: Date,
Expand Down
16 changes: 10 additions & 6 deletions users/authservice/auth-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,28 @@
try {
// Check if required fields are present in the request body
try{
validateRequiredFields(req, ['username', 'password']);
validateRequiredFields(req, ['email', 'username', 'password']);
}
catch(error){
res.status(400).json({ error : error.message });
return
}

const { username, password } = req.body;
const { email, username, password } = req.body;

// Find the user by username in the database
const user = await User.findOne({ username });
let user;
if(username) //Can log in with both
// Find the user by username in the database
user = await User.findOne({ username })
Fixed Show fixed Hide fixed
else
user = await User.findOne({ email })
Fixed Show fixed Hide fixed

// Check if the user exists and verify the password
if (user && await bcrypt.compare(password, user.password)) {
// Generate a JWT token
const token = jwt.sign({ userId: user._id }, 'your-secret-key', { expiresIn: '1h' });
const token = jwt.sign({ userId: user._id }, (process.env.JWT_KEY??'my-key'), { expiresIn: '1h' });
// Respond with the token and user information
res.json({ token: token, username: username});
res.json({ token: token, username: username, email: email});
} else {
res.status(400).json({ error: 'Invalid credentials' });
}
Expand Down
Loading