diff --git a/.gitignore b/.gitignore index 2a57425..b17bcc9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ node_modules/ /.env .vscode/ -rds-db-init.zip +rds-db-init*.zip diff --git a/README.md b/README.md index f13672d..a77213a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Yet-hosted lambda code for initializing RDS Postgres databases without leaving C | DBHost | Primary endpoint of RDS DB cluster. | | DBPort | Port to access RDS DB on. | | DBName | Name of RDS DB to grant user permissions on. | +| DBSchema | Schema to grant the user `USAGE` and `CREATE` on. Defaults to `public`. | ## Supported Regions diff --git a/index.js b/index.js index 08d06a2..e2daf54 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,15 @@ const { Client } = require('pg') var pgformat = require('pg-format'); -const AWS = require("aws-sdk"); -const sm = new AWS.SSM(); +const { SSMClient, GetParameterCommand } = require("@aws-sdk/client-ssm"); const cfnr = require('./cfn-response.js'); +const client = new SSMClient({}); //helper to grab and parse secure strings from ssm const getParam = async (path, secure) => { try { - const param = await sm.getParameter({ - Name: path, - WithDecryption: secure, - }).promise(); - return await param.Parameter.Value; + const command = new GetParameterCommand({ Name: path, WithDecryption: secure }); + const data = await client.send(command); + return data.Parameter.Value; } catch (e) { console.log(e); return null; @@ -31,18 +29,21 @@ exports.handler = async (event, context) => { const appUser = input.DBUsername; const appPass = await getParam(input.DBPasswordPath, true); const db = input.DBName; + const schema = input.DBSchema || 'public'; const client = new Client({ host: input.DBHost, port: input.DBPort, database: db, user: input.DBMasterUsername, - password: await getParam(input.DBMasterPasswordPath, true) + password: await getParam(input.DBMasterPasswordPath, true), + ssl: { rejectUnauthorized: false } }); //needed to use a pg query formtter because you can't use identifiers as vars in prepared statements const checkQuery = 'SELECT FROM pg_catalog.pg_roles WHERE rolname = $1::text'; const createQuery = pgformat('CREATE USER %I WITH ENCRYPTED PASSWORD %L', appUser, appPass); - const grantQuery = pgformat('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', db, appUser); + const grantDatabaseQuery = pgformat('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', db, appUser); + const grantSchemaQuery = pgformat('GRANT USAGE, CREATE ON SCHEMA %I TO %I', schema, appUser); try { console.log("Attempting database connection"); @@ -56,19 +57,22 @@ exports.handler = async (event, context) => { if (userResponse.rowCount < 1) { //create user console.log("Creating User"); - const createResponse = await client.query(createQuery); - //grant db priv to user - console.log("Granting privileges to user."); - const grantResponse = await client.query(grantQuery); + await client.query(createQuery); } else { - console.log("User already exists. Exiting."); + console.log("User already exists."); } + // Reconcile privileges on every create or update so existing users + // receive grants added by newer versions of this function. + console.log("Granting database privileges to user."); + await client.query(grantDatabaseQuery); + console.log("Granting schema privileges to user."); + await client.query(grantSchemaQuery); await client.query("COMMIT"); } catch(err) { console.log("db init transaction failed"); console.error(err); await client.query("ROLLBACK"); - await cfnr.send(event, context, cfnr.FAILED); + return await cfnr.send(event, context, cfnr.FAILED); } console.log("Finished db init"); await cfnr.send(event, context, cfnr.SUCCESS, { diff --git a/package-lock.json b/package-lock.json index 1b60705..3a8e7a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,40 +9,26 @@ "version": "0.0.1", "license": "Apache-2.0", "dependencies": { - "pg": "^8.9.0", + "pg": "^8.23.0", "pg-format": "^1.0.4" } }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/packet-reader": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/pg": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz", - "integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", + "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.2", - "pg-protocol": "^1.6.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -53,9 +39,16 @@ } } }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "optional": true + }, "node_modules/pg-connection-string": { - "version": "2.5.0", - "license": "MIT" + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==" }, "node_modules/pg-format": { "version": "1.0.4", @@ -72,17 +65,17 @@ } }, "node_modules/pg-pool": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", - "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==" }, "node_modules/pg-types": { "version": "2.2.0", @@ -99,10 +92,11 @@ } }, "node_modules/pgpass": { - "version": "1.0.4", - "license": "MIT", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", "dependencies": { - "split2": "^3.1.1" + "split2": "^4.1.0" } }, "node_modules/postgres-array": { @@ -136,54 +130,14 @@ "node": ">=0.10.0" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/split2": { - "version": "3.2.2", - "license": "ISC", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, "node_modules/xtend": { "version": "4.0.2", "license": "MIT", @@ -193,31 +147,29 @@ } }, "dependencies": { - "buffer-writer": { - "version": "2.0.0" - }, - "inherits": { - "version": "2.0.4" - }, - "packet-reader": { - "version": "1.0.0" - }, "pg": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz", - "integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", + "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.2", - "pg-protocol": "^1.6.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "pg-cloudflare": "^1.4.0", + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" } }, + "pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "optional": true + }, "pg-connection-string": { - "version": "2.5.0" + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==" }, "pg-format": { "version": "1.0.4" @@ -226,15 +178,15 @@ "version": "1.0.1" }, "pg-pool": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", - "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", "requires": {} }, "pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==" }, "pg-types": { "version": "2.2.0", @@ -247,9 +199,11 @@ } }, "pgpass": { - "version": "1.0.4", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", "requires": { - "split2": "^3.1.1" + "split2": "^4.1.0" } }, "postgres-array": { @@ -267,31 +221,10 @@ "xtend": "^4.0.0" } }, - "readable-stream": { - "version": "3.6.0", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1" - }, "split2": { - "version": "3.2.2", - "requires": { - "readable-stream": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "util-deprecate": { - "version": "1.0.2" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" }, "xtend": { "version": "4.0.2" diff --git a/package.json b/package.json index be262c9..03cca8f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "author": "Yet Analytics, Inc.", "license": "Apache-2.0", "dependencies": { - "pg": "^8.9.0", + "pg": "^8.23.0", "pg-format": "^1.0.4" } }