From db5cc4b65e8a96afa99f4d07b596cb39b8825a56 Mon Sep 17 00:00:00 2001 From: Benjamin Regonesi Date: Fri, 26 Aug 2022 15:27:32 -0400 Subject: [PATCH] feat(server): support basic auth --- lib/server.js | 6 ++++ package-lock.json | 52 +++++++++++++++++++++++++++++++-- package.json | 1 + test/test.js | 73 ++++++++++++++++++++++++++++++++++++----------- 4 files changed, 113 insertions(+), 19 deletions(-) diff --git a/lib/server.js b/lib/server.js index cc62135..943181f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,6 +1,7 @@ const express = require('express'); const bodyParser = require('body-parser'); const mjml = require('mjml'); +const basicAuth = require('express-basic-auth'); const { logger, middleware: loggingMiddleware } = require('./logging.js'); const packageJson = require('../package.json'); @@ -37,6 +38,7 @@ function handleRequest (req, res) { } module.exports.create = (argv) => { + const { BASIC_AUTH_USERS } = process.env; const config = { keepComments: argv.keepComments, beautify: argv.beautify, @@ -47,6 +49,10 @@ module.exports.create = (argv) => { logger.info('Using configuration:', config); const app = express(); + if (BASIC_AUTH_USERS) { + app.use(basicAuth({ users: JSON.parse(BASIC_AUTH_USERS) })); + } + app.set('mjmlConfig', config); app.use(loggingMiddleware); app.use(bodyParser.text({ diff --git a/package-lock.json b/package-lock.json index a26b91e..0bb0250 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "mjml-http-server", - "version": "0.0.3", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mjml-http-server", - "version": "0.0.3", + "version": "0.1.0", "license": "MIT", "dependencies": { "body-parser": "^1.19.0", "express": "^4.17.1", + "express-basic-auth": "^1.2.1", "express-winston": "^4.0.2", "install": "^0.13.0", "mjml": "^4.12.0", @@ -442,6 +443,22 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1597,6 +1614,14 @@ "node": ">= 0.10.0" } }, + "node_modules/express-basic-auth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz", + "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==", + "dependencies": { + "basic-auth": "^2.0.1" + } + }, "node_modules/express-winston": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", @@ -4968,6 +4993,21 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -5848,6 +5888,14 @@ "vary": "~1.1.2" } }, + "express-basic-auth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz", + "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==", + "requires": { + "basic-auth": "^2.0.1" + } + }, "express-winston": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", diff --git a/package.json b/package.json index c705af8..0687ac1 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "dependencies": { "body-parser": "^1.19.0", "express": "^4.17.1", + "express-basic-auth": "^1.2.1", "express-winston": "^4.0.2", "install": "^0.13.0", "mjml": "^4.12.0", diff --git a/test/test.js b/test/test.js index a7c5824..07c73ed 100644 --- a/test/test.js +++ b/test/test.js @@ -6,20 +6,7 @@ const packageJson = require('../package.json'); const { create } = require('../lib/server.js'); -describe('server', function () { - let server; - let url; - - before(async () => { - server = await create({ validationLevel: 'strict' }).listen(); - url = `http://localhost:${server.address().port}`; - }); - - after(async () => { - await server.close(); - }); - - const mjmlPayload = ` +const mjmlPayload = ` @@ -33,6 +20,19 @@ describe('server', function () { `; +describe('server', function () { + let server; + let url; + + before(async () => { + server = await create({ validationLevel: 'strict' }).listen(); + url = `http://localhost:${server.address().port}`; + }); + + after(async () => { + await server.close(); + }); + it('renders valid mjml', async () => { const res = await makeReq(url, { data: mjmlPayload }); expect(res.status).to.eql(200); @@ -100,11 +100,50 @@ describe('with --max-body', function () { }); }); -const makeReq = (url, { method = 'POST', path = '/v1/render', data = '' } = {}) => { +describe('with basic auth', function () { + let server; + let url; + + const authUsername = 'user'; + const authPassword = 'password'; + + before(async () => { + process.env.BASIC_AUTH_USERS = `{"${authUsername}": "${authPassword}"}`; + server = await create({ validationLevel: 'strict' }).listen(); + url = `http://localhost:${server.address().port}`; + }); + + after(async () => { + await server.close(); + delete process.env.BASIC_AUTH_USERS; + }); + + it('returns 401 on basic auth', async () => { + const res = await makeReq(url); + expect(res.status).to.eql(401); + expect(res.data).to.eql(''); + }); + + it('returns 200 with basic auth', async () => { + const res = await makeReq(url, { + data: mjmlPayload, + auth: { + username: authUsername, + password: authPassword + } + }); + expect(res.status).to.eql(200); + expect(res.data.html).to.include(''); + expect(res.data.mjml).to.eql(mjmlPayload); + }); +}); + +const makeReq = (url, { method = 'POST', path = '/v1/render', data = '', auth = undefined } = {}) => { return axios({ - method: 'POST', + method, url: url + path, data: { mjml: data }, - validateStatus: false + validateStatus: false, + auth }); };