From 479b46eb88e0d15bdfa7fecbf6503be27853acc6 Mon Sep 17 00:00:00 2001 From: Benjamin Petetot Date: Wed, 4 Dec 2024 16:04:10 +0100 Subject: [PATCH] refactor(api): add monitoring on /api/token route --- .../application/monitor-pre-handlers.js | 20 +++++++ .../application/token/token.route.js | 3 +- .../infrastructure/utils/crypto.js | 9 ++++ .../application/monitor-pre-handlers.test.js | 52 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 api/src/identity-access-management/application/monitor-pre-handlers.js create mode 100644 api/src/identity-access-management/infrastructure/utils/crypto.js create mode 100644 api/tests/identity-access-management/unit/application/monitor-pre-handlers.test.js diff --git a/api/src/identity-access-management/application/monitor-pre-handlers.js b/api/src/identity-access-management/application/monitor-pre-handlers.js new file mode 100644 index 00000000000..108b00b0f2d --- /dev/null +++ b/api/src/identity-access-management/application/monitor-pre-handlers.js @@ -0,0 +1,20 @@ +import { logger } from '../../shared/infrastructure/utils/logger.js'; +import { generateHash } from '../infrastructure/utils/crypto.js'; + +async function monitorApiTokenRoute(request, h, dependencies = { logger }) { + const { username, refresh_token, grant_type, scope } = request.payload; + + if (grant_type === 'password') { + const hash = generateHash(username); + dependencies.logger.warn({ hash, grant_type, scope }, 'Authentication attempt'); + } else if (grant_type === 'refresh_token') { + const hash = generateHash(refresh_token); + dependencies.logger.warn({ hash, grant_type, scope }, 'Authentication attempt'); + } else { + dependencies.logger.warn(request.payload, 'Authentication attempt with unknown method'); + } + + return true; +} + +export const monitorPreHandlers = { monitorApiTokenRoute }; diff --git a/api/src/identity-access-management/application/token/token.route.js b/api/src/identity-access-management/application/token/token.route.js index 105a6616d84..1ae110a2cf2 100644 --- a/api/src/identity-access-management/application/token/token.route.js +++ b/api/src/identity-access-management/application/token/token.route.js @@ -2,6 +2,7 @@ import Joi from 'joi'; import { BadRequestError, sendJsonApiError } from '../../../shared/application/http-errors.js'; import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js'; +import { monitorPreHandlers } from '../monitor-pre-handlers.js'; import { tokenController } from './token.controller.js'; export const tokenRoutes = [ @@ -28,7 +29,7 @@ export const tokenRoutes = [ }), ), }, - pre: [{ method: securityPreHandlers.checkIfUserIsBlocked }], + pre: [{ method: monitorPreHandlers.monitorApiTokenRoute }, { method: securityPreHandlers.checkIfUserIsBlocked }], handler: (request, h) => tokenController.createToken(request, h), tags: ['identity-access-management', 'api', 'token'], notes: [ diff --git a/api/src/identity-access-management/infrastructure/utils/crypto.js b/api/src/identity-access-management/infrastructure/utils/crypto.js new file mode 100644 index 00000000000..39aafe3c29f --- /dev/null +++ b/api/src/identity-access-management/infrastructure/utils/crypto.js @@ -0,0 +1,9 @@ +import crypto from 'node:crypto'; + +export function generateHash(data) { + if (!data) return null; + + const hash = crypto.createHash('sha256'); + hash.update(data); + return hash.digest('hex'); +} diff --git a/api/tests/identity-access-management/unit/application/monitor-pre-handlers.test.js b/api/tests/identity-access-management/unit/application/monitor-pre-handlers.test.js new file mode 100644 index 00000000000..cb6cb1a45b3 --- /dev/null +++ b/api/tests/identity-access-management/unit/application/monitor-pre-handlers.test.js @@ -0,0 +1,52 @@ +import { monitorPreHandlers } from '../../../../src/identity-access-management/application/monitor-pre-handlers.js'; +import { generateHash } from '../../../../src/identity-access-management/infrastructure/utils/crypto.js'; +import { expect, hFake, sinon } from '../../../test-helper.js'; + +describe('Unit | Identity Access Management | Application | monitor-pre-handlers', function () { + describe('#monitorApiTokenRoute', function () { + it('logs authentication attempt with grant type password', function () { + // given + const username = 'test@email.com'; + const grant_type = 'password'; + const scope = 'pix-app'; + const hash = generateHash(username); + const logger = { warn: sinon.stub() }; + const request = { payload: { grant_type, username, scope } }; + + // when + monitorPreHandlers.monitorApiTokenRoute(request, hFake, { logger }); + + // then + expect(logger.warn).to.have.been.calledWith({ hash, grant_type, scope }, 'Authentication attempt'); + }); + + it('logs authentication attempt with grant type refresh token', async function () { + // given + const refresh_token = '123'; + const grant_type = 'refresh_token'; + const scope = 'pix-app'; + const hash = generateHash(refresh_token); + const logger = { warn: sinon.stub() }; + const request = { payload: { grant_type, refresh_token, scope } }; + + // when + monitorPreHandlers.monitorApiTokenRoute(request, hFake, { logger }); + + // then + expect(logger.warn).to.have.been.calledWith({ hash, grant_type, scope }, 'Authentication attempt'); + }); + + it('logs authentication attempt with grant type unknown', async function () { + // given + const grant_type = 'unknown'; + const logger = { warn: sinon.stub() }; + const request = { payload: { foo: 'bar', grant_type } }; + + // when + monitorPreHandlers.monitorApiTokenRoute(request, hFake, { logger }); + + // then + expect(logger.warn).to.have.been.calledWith(request.payload, 'Authentication attempt with unknown method'); + }); + }); +});