From 2cf7e6bcbe40e762fabf2f038e4e1188c509887f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Huchet?= Date: Mon, 21 Sep 2020 09:54:40 +0200 Subject: [PATCH] feat: trust SSL certificate by default to ease users onboarding (#472) --- services/database.js | 30 +++-------- templates/app/env.hbs | 2 + templates/app/models/index.hbs | 6 ++- .../sequelize/dumper-output/env.expected | 12 +++++ .../sequelize/dumper-output/index.expected.js | 50 +++++++++++++++++++ test/services/dumper/dumper-sequelize.test.js | 22 ++++++++ 6 files changed, 98 insertions(+), 24 deletions(-) create mode 100644 test-expected/sequelize/dumper-output/env.expected create mode 100644 test-expected/sequelize/dumper-output/index.expected.js diff --git a/services/database.js b/services/database.js index 2c9d7faf..4fe43d8e 100644 --- a/services/database.js +++ b/services/database.js @@ -57,7 +57,6 @@ function Database() { this.connect = (options) => { const isSSL = options.dbSSL || options.ssl; - let connection; const databaseDialect = getDialect(options.dbConnectionUrl, options.dbDialect); if (databaseDialect === 'mongodb') { @@ -66,30 +65,15 @@ function Database() { const connectionOptionsSequelize = { logging: false }; - // NOTICE: mysql2 does not accepts unwanted options anymore. - // See: https://github.com/sidorares/node-mysql2/pull/895 - if (databaseDialect === 'mysql') { - // NOTICE: Add SSL options only if the user selected SSL mode. - if (isSSL) { - // TODO: Lumber should accept certificate file (CRT) to work with SSL. - // Since it requires to review onboarding, it is not implemented yet. - // See: https://www.npmjs.com/package/mysql#ssl-options - connectionOptionsSequelize.dialectOptions = { - ssl: { rejectUnauthorized: isSSL }, - }; - } - } else if (databaseDialect === 'mssql') { - connectionOptionsSequelize.dialectOptions = { - options: { - encrypt: isSSL, - }, - }; - } else { - connectionOptionsSequelize.dialectOptions = { - ssl: isSSL, - }; + if (databaseDialect === 'mssql') { + connectionOptionsSequelize.dialectOptions = { options: { encrypt: isSSL } }; + } else if (isSSL) { + // Add SSL options only if the user selected SSL mode. + // SSL Cerificate is always trusted during `lumber generate` command to ease their onboarding. + connectionOptionsSequelize.dialectOptions = { ssl: { rejectUnauthorized: false } }; } + let connection; if (options.dbConnectionUrl) { connection = new Sequelize(options.dbConnectionUrl, connectionOptionsSequelize); } else { diff --git a/templates/app/env.hbs b/templates/app/env.hbs index 14b45a06..90877d93 100644 --- a/templates/app/env.hbs +++ b/templates/app/env.hbs @@ -7,6 +7,8 @@ DATABASE_URL={{ databaseUrl }} DATABASE_SCHEMA={{ dbSchema }} {{/if}} DATABASE_SSL={{ ssl }} +# This should be removed in production environment. +DATABASE_REJECT_UNAUTHORIZED=false FOREST_AUTH_SECRET={{ forestAuthSecret }} FOREST_ENV_SECRET={{ forestEnvSecret }} diff --git a/templates/app/models/index.hbs b/templates/app/models/index.hbs index 4f596df3..07dc50e6 100644 --- a/templates/app/models/index.hbs +++ b/templates/app/models/index.hbs @@ -58,7 +58,11 @@ if (process.env.DATABASE_SSL && JSON.parse(process.env.DATABASE_SSL.toLowerCase( {{else if isMSSQL}} databaseOptions.dialectOptions.options = { encrypt: true }; {{else}} - databaseOptions.dialectOptions.ssl = true; + if (process.env.DATABASE_REJECT_UNAUTHORIZED === false) { + databaseOptions.dialectOptions.ssl = { rejectUnauthorized: false }; + } else { + databaseOptions.dialectOptions.ssl = true; + } {{/if}} } diff --git a/test-expected/sequelize/dumper-output/env.expected b/test-expected/sequelize/dumper-output/env.expected new file mode 100644 index 00000000..9d1ca0a6 --- /dev/null +++ b/test-expected/sequelize/dumper-output/env.expected @@ -0,0 +1,12 @@ +APPLICATION_PORT=1654 + +CORS_ORIGINS= + +DATABASE_URL=postgres://localhost:27017 +DATABASE_SCHEMA=public +DATABASE_SSL=false +# This should be removed in production environment. +DATABASE_REJECT_UNAUTHORIZED=false + +FOREST_AUTH_SECRET= +FOREST_ENV_SECRET= diff --git a/test-expected/sequelize/dumper-output/index.expected.js b/test-expected/sequelize/dumper-output/index.expected.js new file mode 100644 index 00000000..6df3343a --- /dev/null +++ b/test-expected/sequelize/dumper-output/index.expected.js @@ -0,0 +1,50 @@ +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); + +if (!process.env.DATABASE_URL) { + console.error('Cannot connect to the database. Please declare the DATABASE_URL environment variable with the correct database connection string.'); + process.exit(); +} + +const databaseOptions = { + logging: process.env.NODE_ENV === 'development' ? console.log : false, + pool: { maxConnections: 10, minConnections: 1 }, + dialectOptions: {}, +}; + +if (process.env.DATABASE_SSL && JSON.parse(process.env.DATABASE_SSL.toLowerCase())) { + if (process.env.DATABASE_REJECT_UNAUTHORIZED === false) { + databaseOptions.dialectOptions.ssl = { rejectUnauthorized: false }; + } else { + databaseOptions.dialectOptions.ssl = true; + } +} + +const sequelize = new Sequelize(process.env.DATABASE_URL, databaseOptions); +const db = {}; + +fs + .readdirSync(__dirname) + .filter((file) => { + return (file.indexOf('.') !== 0) && (file !== 'index.js'); + }) + .forEach((file) => { + try { + const model = sequelize.import(path.join(__dirname, file)); + db[model.name] = model; + } catch (error) { + console.error('Model creation error: ' + error); + } + }); + +Object.keys(db).forEach((modelName) => { + if ('associate' in db[modelName]) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/test/services/dumper/dumper-sequelize.test.js b/test/services/dumper/dumper-sequelize.test.js index de3d1d89..de6b4574 100644 --- a/test/services/dumper/dumper-sequelize.test.js +++ b/test/services/dumper/dumper-sequelize.test.js @@ -84,4 +84,26 @@ describe('services > dumper > sequelize', () => { expect(generatedFile).toStrictEqual(expectedFile); cleanOutput(); }); + + it('should generate the model index file', async () => { + expect.assertions(1); + const dumper = await getDumper(); + await dumper.dump(simpleModel); + const generatedFile = fs.readFileSync('./test-output/sequelize/models/index.js', 'utf8'); + const expectedFile = fs.readFileSync('./test-expected/sequelize/dumper-output/index.expected.js', 'utf-8'); + + expect(generatedFile).toStrictEqual(expectedFile); + cleanOutput(); + }); + + it('should generate the env file', async () => { + expect.assertions(1); + const dumper = await getDumper(); + await dumper.dump(simpleModel); + const generatedFile = fs.readFileSync('./test-output/sequelize/.env', 'utf8'); + const expectedFile = fs.readFileSync('./test-expected/sequelize/dumper-output/env.expected', 'utf-8'); + + expect(generatedFile).toStrictEqual(expectedFile); + cleanOutput(); + }); });