block retries (#144)

* block retries

* block retries

* fixed logginAttempsBlocked typo

---------

Co-authored-by: p.souza <p.souza@cognigy.com>
This commit is contained in:
Paulo Telles
2023-04-05 16:47:25 +02:00
committed by GitHub
parent 127b690ae2
commit 8f93b69af0
5 changed files with 111 additions and 2 deletions

2
app.js
View File

@@ -38,6 +38,7 @@ const {
addKey,
retrieveKey,
deleteKey,
incrKey
} = require('./lib/helpers/realtimedb-helpers');
const {
getTtsVoices
@@ -76,6 +77,7 @@ app.locals = {
purgeCalls,
retrieveSet,
addKey,
incrKey,
retrieveKey,
deleteKey,
getTtsVoices,

View File

@@ -9,6 +9,7 @@ const {
addKey,
retrieveKey,
deleteKey,
incrKey,
client: redisClient,
} = require('@jambonz/realtimedb-helpers')({
host: process.env.JAMBONES_REDIS_HOST || 'localhost',
@@ -25,4 +26,5 @@ module.exports = {
retrieveKey,
deleteKey,
redisClient,
incrKey
};

View File

@@ -17,7 +17,7 @@ const tokenSql = 'SELECT token from api_keys where account_sid IS NULL AND servi
router.post('/', async(req, res) => {
const logger = req.app.locals.logger;
const {logger, incrKey, retrieveKey} = req.app.locals;
const {username, password} = req.body;
if (!username || !password) {
logger.info('Bad POST to /login is missing username or password');
@@ -31,8 +31,28 @@ router.post('/', async(req, res) => {
return res.sendStatus(403);
}
logger.info({r}, 'successfully retrieved user account');
const maxLoginAttempts = process.env.LOGIN_ATTEMPTS_MAX_RETRIES || 6;
const loginAttempsBlocked = await retrieveKey(`login:${r[0].user_sid}`) >= maxLoginAttempts;
if (loginAttempsBlocked) {
logger.info(`User ${r[0].user_sid} was blocked due to excessive login attempts with incorrect credentials.`);
return res.status(403)
.json({error: 'Maximum login attempts reached. Please try again later or reset your password.'});
}
const isCorrect = await verifyPassword(r[0].hashed_password, password);
if (!isCorrect) return res.sendStatus(403);
if (!isCorrect) {
const attempTime = process.env.LOGIN_ATTEMPTS_TIME || 1800;
const newAttempt = await incrKey(`login:${r[0].user_sid}`, attempTime)
.catch((err) => logger.error({err}, 'Error adding logging attempt to redis'));
if (newAttempt >= maxLoginAttempts) {
logger.info(`User ${r[0].user_sid} is now blocked due to excessive login attempts with incorrect credentials.`);
return res.status(403)
.json({error: `Maximum login attempts reached. Please try again in ${attempTime} seconds.`});
}
return res.sendStatus(403);
}
const force_change = !!r[0].force_change;
const [t] = await promisePool.query(tokenSql);
if (t.length === 0) {

View File

@@ -13,6 +13,7 @@ require('./ms-teams');
require('./speech-credentials');
require('./recent-calls');
require('./users');
require('./login');
require('./webapp_tests');
// require('./homer');
require('./call-test');

84
test/login.js Normal file
View File

@@ -0,0 +1,84 @@
const test = require('tape') ;
const jwt = require('jsonwebtoken');
const request = require('request-promise-native').defaults({
baseUrl: 'http://127.0.0.1:3000/v1'
});
const exec = require('child_process').exec ;
const {generateHashedPassword} = require('../lib/utils/password-utils');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
test('add an admin user', (t) => {
exec(`${__dirname}/../db/reset_admin_password.js`, (err, stdout, stderr) => {
console.log(stderr);
console.log(stdout);
if (err) return t.end(err);
t.pass('successfully added admin user');
t.end();
});
});
test('login tests', async(t) => {
const app = require('../app');
const password = 'abcde12345-';
try {
let result;
/* login as admin to get a jwt */
result = await request.post('/login', {
resolveWithFullResponse: true,
json: true,
body: {
username: 'admin',
password: 'admin',
}
});
t.ok(result.statusCode === 200 && result.body.token, 'successfully logged in as admin');
const maxAttempts = process.env.LOGIN_ATTEMPTS_MAX_RETRIES || 6;
const attempTime = process.env.LOGIN_ATTEMPTS_TIME || 1800;
for (let index = 0; index <= maxAttempts; index++) {
if (index === (maxAttempts - 1)) {
attemptResult = await request.post('/login', {
resolveWithFullResponse: true,
json: true,
body: {
username: 'admin',
password: 'adm',
}
}).catch(error => {
t.ok(error.response.statusCode === 403, `Maximum login attempts reached. Please try again in ${attempTime} seconds.`)
});
} else if (index < maxAttempts) {
attemptResult = await request.post('/login', {
resolveWithFullResponse: true,
json: true,
body: {
username: 'admin',
password: 'adm',
}
}).catch(error => t.ok(error.response.statusCode === 403));
} else {
attemptResult = await request.post('/login', {
resolveWithFullResponse: true,
json: true,
body: {
username: 'admin',
password: 'adm',
}
}).catch(error => t.ok(error.response.statusCode === 403, 'Maximum login attempts reached. Please try again later or reset your password.'));
}
}
} catch (err) {
console.error(err);
t.end(err);
}
});