From 81cb488d350cbe7a2682621781515256624063ad Mon Sep 17 00:00:00 2001 From: vmaubert Date: Wed, 18 Dec 2024 14:17:53 +0100 Subject: [PATCH] =?UTF-8?q?feat(emails):=20ajoute=20un=20CRON=C2=A0pour=20?= =?UTF-8?q?v=C3=A9rifier=20les=20emails=20des=20laboratoires=20(#82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 ++- .github/workflows/github-actions.yml | 3 ++ README.md | 1 + cron.json | 8 +++++ .../checkLaboratoryEmailsController.ts | 14 ++++++++ server/middlewares/checks/authCheck.ts | 26 +++++++++++++- server/middlewares/test/authCheck.test.ts | 34 +++++++++++++++++++ server/routers/m2mProtected.ts | 8 +++++ server/routers/protected.ts | 31 ++++++++--------- server/server.ts | 4 ++- server/utils/config.ts | 7 ++++ 11 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 cron.json create mode 100644 server/controllers/checkLaboratoryEmailsController.ts create mode 100644 server/middlewares/test/authCheck.test.ts create mode 100644 server/routers/m2mProtected.ts diff --git a/.env.example b/.env.example index 27446937..aacd6e69 100644 --- a/.env.example +++ b/.env.example @@ -17,4 +17,6 @@ S3_BUCKET=maestro MAILER_HOST=localhost MAILER_PORT=1025 MAILER_USER= -MAILER_PASSWORD= \ No newline at end of file +MAILER_PASSWORD= + +M2M_BASIC_TOKEN=goodtoken \ No newline at end of file diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index ea90423a..988ba437 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -67,6 +67,7 @@ jobs: S3_REGION: region S3_ACCESS_KEY_ID: key S3_SECRET_ACCESS_KEY: secret + M2M_BASIC_TOKEN: fakeBasicToken - name: Frontend linter run: npm --prefix frontend run lint - name: Server linter @@ -78,8 +79,10 @@ jobs: # env: # AUTH_SECRET: abcd1234 # DATABASE_URL: postgres://postgres:postgres@localhost:5432/maestro +# M2M_BASIC_TOKEN: fakeBasicToken - name: Server tests run: npm run test env: AUTH_SECRET: abcd1234 DATABASE_URL: postgres://postgres:postgres@localhost:5432/maestro + M2M_BASIC_TOKEN: fakeBasicToken diff --git a/README.md b/README.md index 9581235f..00ff96af 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY S3_BUCKET S3_FILE_PATH +M2M_BASIC_TOKEN REACT_APP_PUBLIC_URL REACT_APP_API_URL diff --git a/cron.json b/cron.json new file mode 100644 index 00000000..c0e33247 --- /dev/null +++ b/cron.json @@ -0,0 +1,8 @@ +{ + "jobs": [ + { + "command": "*/10 * * * * curl ${REACT_APP_API_URL}/api/m2m/checkLaboratoryEmails -H \"authorization: ${M2M_BASIC_TOKEN}\"", + "size": "M" + } + ] +} \ No newline at end of file diff --git a/server/controllers/checkLaboratoryEmailsController.ts b/server/controllers/checkLaboratoryEmailsController.ts new file mode 100644 index 00000000..4a9764c3 --- /dev/null +++ b/server/controllers/checkLaboratoryEmailsController.ts @@ -0,0 +1,14 @@ +import { constants } from 'http2'; +import {Request, Response} from 'express'; + +export const checkLaboratoryEmails = async (_request: Request, response: Response): Promise => { + + + console.info('Cheking emails...'); + + + //TODO + + + response.status(constants.HTTP_STATUS_OK).send('OK'); +}; diff --git a/server/middlewares/checks/authCheck.ts b/server/middlewares/checks/authCheck.ts index f9d11ff5..e9c38900 100644 --- a/server/middlewares/checks/authCheck.ts +++ b/server/middlewares/checks/authCheck.ts @@ -1,4 +1,4 @@ -import { NextFunction, Request, Response } from 'express'; +import express, { NextFunction, Request, Response } from 'express'; import { expressjwt } from 'express-jwt'; import _ from 'lodash'; @@ -11,6 +11,7 @@ import { UserPermission } from '../../../shared/schema/User/UserPermission'; import { UserRole } from '../../../shared/schema/User/UserRole'; import userRepository from '../../repositories/userRepository'; import config from '../../utils/config'; +import { constants } from 'http2'; export const jwtCheck = (credentialsRequired: boolean) => expressjwt({ @@ -69,3 +70,26 @@ export const permissionsCheck = (permissions: UserPermission[]) => next(); }; + +export const basicAuthCheck = + async (req: Request, res: express.Response, next: express.NextFunction) => { + try { + const token = req.headers.authorization + if (token !== config.m2mBasicToken) { + res.status(constants.HTTP_STATUS_UNAUTHORIZED); + res.send('Authentication Required'); + + return; + } + + } catch (e) { + console.error(e); + res.status(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR); + res.send('Internal error'); + + return; + } + + + next() + }; diff --git a/server/middlewares/test/authCheck.test.ts b/server/middlewares/test/authCheck.test.ts new file mode 100644 index 00000000..52281e82 --- /dev/null +++ b/server/middlewares/test/authCheck.test.ts @@ -0,0 +1,34 @@ +import express from 'express'; +import { constants } from 'http2'; +import request from 'supertest'; +import { describe, test } from 'vitest'; +import config from '../../utils/config'; +import { basicAuthCheck } from '../checks/authCheck'; + +describe('basicAuth', () => { + const app = express(); + + const myM2MRoute = '/basic-auth-route'; + app.use(myM2MRoute, basicAuthCheck, (_request, response) => + response.sendStatus(constants.HTTP_STATUS_OK) + ); + + test('should respond with the status 200 with good basic token', async () => { + await request(app) + .get(myM2MRoute) + .set('authorization', config.m2mBasicToken) + .expect(constants.HTTP_STATUS_OK); + }); + + test('should respond with the status 401 with wrong basic token', async () => { + await request(app) + .get(myM2MRoute) + .set('authorization', 'pas bon token') + .expect(constants.HTTP_STATUS_UNAUTHORIZED); + }); + test('should respond with the status 401 with no basic token', async () => { + await request(app) + .get(myM2MRoute) + .expect(constants.HTTP_STATUS_UNAUTHORIZED); + }); +}); diff --git a/server/routers/m2mProtected.ts b/server/routers/m2mProtected.ts new file mode 100644 index 00000000..181aa11f --- /dev/null +++ b/server/routers/m2mProtected.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import { checkLaboratoryEmails } from '../controllers/checkLaboratoryEmailsController'; +import { basicAuthCheck } from '../middlewares/checks/authCheck'; + +export const m2mProtectedRouter = express.Router(); + +m2mProtectedRouter.use(basicAuthCheck); +m2mProtectedRouter.use('/checkLaboratoryEmails', checkLaboratoryEmails) diff --git a/server/routers/protected.ts b/server/routers/protected.ts index cd87ab31..9a6bfda0 100644 --- a/server/routers/protected.ts +++ b/server/routers/protected.ts @@ -13,26 +13,25 @@ import sampleRouter from './sample.router'; import substanceRouter from './substance.router'; import userRouter from './user.router'; -const router = express.Router(); +export const protectedRouter = express.Router(); -router.use(jwtCheck(true)); -router.use(userCheck(true)); +protectedRouter.use(jwtCheck(true)); +protectedRouter.use(userCheck(true)); -router.use('/analysis', analysisRouter); -router.use('/addresses', addressRouter); -router.use('/companies', companyRouter); -router.use('/documents', documentRouter); -router.use('/laboratories', laboratoryRouter); -router.use('/prescriptions', prescriptionRouter); -router.use('/prescriptions', regionalPrescriptionRouter); -router.use('/programming-plans', programmingPlanRouter); -router.use('/samples', sampleRouter); -router.use('/substances', substanceRouter); -router.use('/users', userRouter); +protectedRouter.use('/analysis', analysisRouter); +protectedRouter.use('/addresses', addressRouter); +protectedRouter.use('/companies', companyRouter); +protectedRouter.use('/documents', documentRouter); +protectedRouter.use('/laboratories', laboratoryRouter); +protectedRouter.use('/prescriptions', prescriptionRouter); +protectedRouter.use('/prescriptions', regionalPrescriptionRouter); +protectedRouter.use('/programming-plans', programmingPlanRouter); +protectedRouter.use('/samples', sampleRouter); +protectedRouter.use('/substances', substanceRouter); +protectedRouter.use('/users', userRouter); -router.get('/regions.geojson', (req, res) => { +protectedRouter.get('/regions.geojson', (req, res) => { res.setHeader('Content-Type', 'application/json'); fs.createReadStream(__dirname + '/../data/regions.json').pipe(res); }); -export default router; diff --git a/server/server.ts b/server/server.ts index 34d8dccb..65de2cb2 100644 --- a/server/server.ts +++ b/server/server.ts @@ -9,10 +9,11 @@ import helmet from 'helmet'; import path from 'path'; import RouteNotFoundError from './errors/routeNotFoundError'; import errorHandler from './middlewares/error-handler'; -import protectedRouter from './routers/protected'; +import { protectedRouter } from './routers/protected'; import unprotectedRouter from './routers/unprotected'; import config from './utils/config'; import sentry from './utils/sentry'; +import { m2mProtectedRouter } from './routers/m2mProtected'; const PORT = config.serverPort; @@ -80,6 +81,7 @@ export function createServer(): Server { app.use(rateLimiter); app.set('trust proxy', 1); + app.use('/api/m2m', m2mProtectedRouter); app.use('/api', unprotectedRouter); app.use('/api', protectedRouter); diff --git a/server/utils/config.ts b/server/utils/config.ts index f16892e3..57c3b1ed 100644 --- a/server/utils/config.ts +++ b/server/utils/config.ts @@ -84,6 +84,7 @@ interface Config { url: string; }; }; + m2mBasicToken: string } const config = convict({ @@ -282,6 +283,12 @@ const config = convict({ default: 'https://recherche-entreprises.api.gouv.fr' } } + }, + m2mBasicToken: { + env: 'M2M_BASIC_TOKEN', + format: String, + sensitive: true, + default: null } }) .validate({ allowed: 'strict' })