UNPKG

postgraphile

Version:

A GraphQL schema created by reflection over a PostgreSQL schema 🐘 (previously known as PostGraphQL)

466 lines 37.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.debugPgClient = void 0; const createDebugger = require("debug"); const jwt = require("jsonwebtoken"); const graphql_1 = require("graphql"); const sql = require("pg-sql2"); const pgClientFromContext_1 = require("../postgres/inventory/pgClientFromContext"); const pluginHook_1 = require("./pluginHook"); const postgraphile_core_1 = require("postgraphile-core"); const undefinedIfEmpty = (o) => o && (!Array.isArray(o) || o.length) ? o : undefined; const debugPg = createDebugger('postgraphile:postgres'); const debugPgError = createDebugger('postgraphile:postgres:error'); const debugPgNotice = createDebugger('postgraphile:postgres:notice'); /** * Formats an error/notice from `pg` and feeds it into a `debug` function. */ function debugPgErrorObject(debugFn, object) { debugFn('%s%s: %s%s%s', object.severity || 'ERROR', object.code ? `[${object.code}]` : '', object.message || object, object.where ? ` | WHERE: ${object.where}` : '', object.hint ? ` | HINT: ${object.hint}` : ''); } function swallowErrors() { /* noop */ } const simpleWithPgClientCache = new WeakMap(); function simpleWithPgClient(pgPool) { const cached = simpleWithPgClientCache.get(pgPool); if (cached) { return cached; } const func = async (cb) => { const pgClient = await pgPool.connect(); pgClient.on('error', swallowErrors); try { return await cb(pgClient); } finally { pgClient.removeListener('error', swallowErrors); pgClient.release(); } }; simpleWithPgClientCache.set(pgPool, func); return func; } const withDefaultPostGraphileContext = async (options, callback) => { const { pgPool, jwtToken, jwtSecret, jwtPublicKey, jwtAudiences, jwtRole = ['role'], jwtVerifyOptions, pgDefaultRole, pgSettings, explain, queryDocumentAst, operationName, pgForceTransaction, singleStatement, } = options; let operation; if (!pgForceTransaction && queryDocumentAst) { // tslint:disable-next-line for (let i = 0, l = queryDocumentAst.definitions.length; i < l; i++) { const definition = queryDocumentAst.definitions[i]; if (definition.kind === graphql_1.Kind.OPERATION_DEFINITION) { if (!operationName && operation) { throw new Error('Multiple operations present in GraphQL query, you must specify an `operationName` so we know which one to execute.'); } else if (!operationName || (definition.name && definition.name.value === operationName)) { operation = definition; } } } } // Warning: this is only set if pgForceTransaction is falsy const operationType = operation != null ? operation.operation : null; const { role: pgRole, localSettings, jwtClaims } = await getSettingsForPgClientTransaction({ jwtToken, jwtSecret, jwtPublicKey, jwtAudiences, jwtRole, jwtVerifyOptions, pgDefaultRole, pgSettings, }); const sqlSettings = []; if (localSettings.length > 0) { // Later settings should win, so we're going to loop backwards and not // add settings for keys we've already seen. const seenKeys = []; // TODO:perf: looping backwards is slow for (let i = localSettings.length - 1; i >= 0; i--) { const [key, value] = localSettings[i]; if (!seenKeys.includes(key)) { seenKeys.push(key); // Make sure that the third config is always `true` so that we are only // ever setting variables on the transaction. // Also, we're using `unshift` to undo the reverse-looping we're doing sqlSettings.unshift(sql.fragment `set_config(${sql.value(key)}, ${sql.value(value)}, true)`); } } } const sqlSettingsQuery = sqlSettings.length > 0 ? sql.compile(sql.query `select ${sql.join(sqlSettings, ', ')}`) : null; // If we can avoid transactions, we get greater performance. const needTransaction = pgForceTransaction || !!sqlSettingsQuery || (operationType !== 'query' && operationType !== 'subscription'); // Now we've caught as many errors as we can at this stage, let's create a DB connection. const withAuthenticatedPgClient = !needTransaction ? simpleWithPgClient(pgPool) : async (cb) => { // Connect a new Postgres client const pgClient = await pgPool.connect(); pgClient.on('error', swallowErrors); try { // Begin our transaction await pgClient.query('begin'); try { // If there is at least one local setting, load it into the database. if (sqlSettingsQuery) { await pgClient.query(sqlSettingsQuery); } // Use the client, wait for it to be finished with, then go to 'finally' return await cb(pgClient); } finally { // Cleanup our Postgres client by ending the transaction and releasing // the client back to the pool. Always do this even if the query fails. await pgClient.query('commit'); } } finally { pgClient.removeListener('error', swallowErrors); pgClient.release(); } }; if (singleStatement) { // TODO:v5: remove this workaround /* * This is a workaround for subscriptions; the GraphQL context is allocated * for the entire duration of the subscription, however hogging a pgClient * for more than a few milliseconds (let alone hours!) is a no-no. So we * fake a PG client that will set up the transaction each time `query` is * called. It's a very thin/dumb wrapper, so it supports nothing but * `query`. */ const fakePgClient = { query(textOrQueryOptions, values, // tslint:disable-line no-any cb) { if (!textOrQueryOptions) { throw new Error('Incompatible call to singleStatement - no statement passed?'); } else if (typeof textOrQueryOptions === 'object') { if (values || cb) { throw new Error('Incompatible call to singleStatement - expected no callback'); } } else if (typeof textOrQueryOptions !== 'string') { throw new Error('Incompatible call to singleStatement - bad query'); } else if (values && !Array.isArray(values)) { throw new Error('Incompatible call to singleStatement - bad values'); } else if (cb) { throw new Error('Incompatible call to singleStatement - expected to return promise'); } // Generate an authenticated client on the fly return withAuthenticatedPgClient(pgClient => pgClient.query(textOrQueryOptions, values)); }, }; // tslint:disable-line no-any return callback({ [pgClientFromContext_1.$$pgClient]: fakePgClient, pgRole, jwtClaims, }); } else { return withAuthenticatedPgClient(async (pgClient) => { let results = null; if (explain) { pgClient.startExplain(); } try { return await callback({ [pgClientFromContext_1.$$pgClient]: pgClient, pgRole, jwtClaims, ...(explain ? { getExplainResults: () => { results = results || pgClient.stopExplain(); return results; }, } : null), }); } finally { if (explain) { results = results || pgClient.stopExplain(); } } }); } }; /** * Creates a PostGraphile context object which should be passed into a GraphQL * execution. This function will also connect a client from a Postgres pool and * setup a transaction in that client. * * This function is intended to wrap a call to GraphQL-js execution like so: * * ```js * const result = await withPostGraphileContext({ * pgPool, * jwtToken, * jwtSecret, * pgDefaultRole, * }, async context => { * return await graphql( * schema, * query, * null, * { ...context }, * variables, * operationName, * ); * }); * ``` */ const withPostGraphileContext = async (options, callback) => { const pluginHook = pluginHook_1.pluginHookFromOptions(options); const withContext = pluginHook('withPostGraphileContext', withDefaultPostGraphileContext, { options, }); return withContext(options, callback); }; exports.default = withPostGraphileContext; /** * Sets up the Postgres client transaction by decoding the JSON web token and * doing some other cool things. */ // THIS METHOD SHOULD NEVER RETURN EARLY. If this method returns early then it // may skip the super important step of setting the role on the Postgres // client. If this happens it’s a huge security vulnerability. Never using the // keyword `return` in this function is a good first step. You can still throw // errors, however, as this will stop the request execution. async function getSettingsForPgClientTransaction({ jwtToken, jwtSecret, jwtPublicKey, jwtAudiences, jwtRole, jwtVerifyOptions, pgDefaultRole, pgSettings, }) { // Setup our default role. Once we decode our token, the role may change. let role = pgDefaultRole; let jwtClaims = {}; // If we were provided a JWT token, let us try to verify it. If verification // fails we want to throw an error. if (jwtToken) { // Try to run `jwt.verify`. If it fails, capture the error and re-throw it // as a 403 error because the token is not trustworthy. try { const jwtVerificationSecret = jwtPublicKey || jwtSecret; // If a JWT token was defined, but a secret was not provided to the server or // secret had unsupported type, throw a 403 error. if (!Buffer.isBuffer(jwtVerificationSecret) && typeof jwtVerificationSecret !== 'string' && typeof jwtVerificationSecret !== 'function') { // tslint:disable-next-line no-console console.error(`ERROR: '${jwtPublicKey ? 'jwtPublicKey' : 'jwtSecret'}' was not set to a string or buffer - rejecting JWT-authenticated request.`); throw new Error('Not allowed to provide a JWT token.'); } if (jwtAudiences != null && jwtVerifyOptions && 'audience' in jwtVerifyOptions) throw new Error(`Provide either 'jwtAudiences' or 'jwtVerifyOptions.audience' but not both`); const claims = await new Promise((resolve, reject) => { jwt.verify(jwtToken, jwtVerificationSecret, { ...jwtVerifyOptions, audience: jwtAudiences || (jwtVerifyOptions && 'audience' in jwtVerifyOptions ? undefinedIfEmpty(jwtVerifyOptions.audience) : ['postgraphile']), }, (err, decoded) => { if (err) reject(err); else resolve(decoded); }); }); if (typeof claims === 'string') { throw new Error('Invalid JWT payload'); } // jwt.verify returns `object | string`; but the `object` part is really a map jwtClaims = claims; const roleClaim = getPath(jwtClaims, jwtRole); // If there is a `role` property in the claims, use that instead of our // default role. if (typeof roleClaim !== 'undefined') { if (typeof roleClaim !== 'string') throw new Error(`JWT \`role\` claim must be a string. Instead found '${typeof jwtClaims['role']}'.`); role = roleClaim; } } catch (error) { // In case this error is thrown in an HTTP context, we want to add status code // Note. jwt.verify will add a name key to its errors. (https://github.com/auth0/node-jsonwebtoken#errors--codes) error.statusCode = 'name' in error && error.name === 'TokenExpiredError' ? // The correct status code for an expired ( but otherwise acceptable token is 401 ) 401 : // All other authentication errors should get a 403 status code. 403; throw error; } } // Instantiate a map of local settings. This map will be transformed into a // Sql query. const localSettings = []; // Set the custom provided settings before jwt claims and role are set // this prevents an accidentional overwriting if (pgSettings && typeof pgSettings === 'object') { for (const key in pgSettings) { if (Object.prototype.hasOwnProperty.call(pgSettings, key) && isPgSettingValid(pgSettings[key])) { if (key === 'role') { role = String(pgSettings[key]); } else { localSettings.push([key, String(pgSettings[key])]); } } } } // If there is a rule, we want to set the root `role` setting locally // to be our role. The role may only be null if we have no default role. if (typeof role === 'string') { localSettings.push(['role', role]); } // If we have some JWT claims, we want to set those claims as local // settings with the namespace `jwt.claims`. for (const key in jwtClaims) { if (Object.prototype.hasOwnProperty.call(jwtClaims, key)) { const rawValue = jwtClaims[key]; // Unsafe to pass raw object/array to pg.query -> set_config; instead JSONify const value = rawValue != null && typeof rawValue === 'object' ? JSON.stringify(rawValue) : rawValue; if (isPgSettingValid(value)) { localSettings.push([`jwt.claims.${key}`, String(value)]); } } } return { localSettings, role, jwtClaims: jwtToken ? jwtClaims : null, }; } const $$pgClientOrigQuery = Symbol(); /** * Monkey-patches the `query` method of a pg Client to add debugging * functionality. Use with care. */ function debugPgClient(pgClient, allowExplain = false) { // If Postgres debugging is enabled, enhance our query function by adding // a debug statement. if (!pgClient[$$pgClientOrigQuery]) { // Set the original query method to a key on our client. If that key is // already set, use that. pgClient[$$pgClientOrigQuery] = pgClient.query; pgClient.startExplain = () => { pgClient._explainResults = []; }; pgClient.stopExplain = async () => { const results = pgClient._explainResults; pgClient._explainResults = null; if (!results) { return Promise.resolve([]); } return (await Promise.all(results.map(async (r) => { const { result: resultPromise, ...rest } = r; const result = await resultPromise; const firstKey = result && result[0] && Object.keys(result[0])[0]; if (!firstKey) { return null; } const plan = result.map((r) => r[firstKey]).join('\n'); return { ...rest, plan, }; }))).filter((entry) => !!entry); }; if (debugPgNotice.enabled) { pgClient.on('notice', (msg) => { debugPgErrorObject(debugPgNotice, msg); }); } const logError = (error) => { if (error.name && error['severity']) { debugPgErrorObject(debugPgError, error); } else { debugPgError('%O', error); } }; if (debugPg.enabled || debugPgNotice.enabled || allowExplain) { // tslint:disable-next-line only-arrow-functions pgClient.query = function (...args) { const [a, b, c] = args; // If we understand it (and it uses the promises API) if ((typeof a === 'string' && !c && (!b || Array.isArray(b))) || (typeof a === 'object' && !b && !c)) { if (debugPg.enabled) { // Debug just the query text. We don’t want to debug variables because // there may be passwords in there. debugPg('%s', postgraphile_core_1.formatSQLForDebugging(a && a.text ? a.text : a)); } if (pgClient._explainResults) { const query = a && a.text ? a.text : a; const values = a && a.text ? a.values : b; if (query.match(/^\s*(select|insert|update|delete|with)\s/i) && !query.includes(';')) { // Explain it const explain = `explain ${query}`; pgClient._explainResults.push({ query, result: pgClient[$$pgClientOrigQuery] .call(this, explain, values) .then((data) => data.rows) // swallow errors during explain .catch(() => null), }); } } const promiseResult = pgClient[$$pgClientOrigQuery].apply(this, args); if (debugPgError.enabled) { // Report the error with our Postgres debugger. promiseResult.catch(logError); } return promiseResult; } else { // We don't understand it (e.g. `pgPool.query`), just let it happen. return pgClient[$$pgClientOrigQuery].apply(this, args); } }; } } return pgClient; } exports.debugPgClient = debugPgClient; /** * Safely gets the value at `path` (array of keys) of `inObject`. * * @private */ function getPath(inObject, path) { let object = inObject; // From https://github.com/lodash/lodash/blob/master/.internal/baseGet.js let index = 0; const length = path.length; while (object && index < length) { object = object[path[index++]]; } return index && index === length ? object : undefined; } /** * Check if a pgSetting is a string or a number. * Null and Undefined settings are not valid and will be ignored. * pgSettings of other types throw an error. * * @private */ function isPgSettingValid(pgSetting) { if (pgSetting === undefined || pgSetting === null) { return false; } const typeOfPgSetting = typeof pgSetting; if (typeOfPgSetting === 'string' || typeOfPgSetting === 'number' || typeOfPgSetting === 'boolean') { return true; } // TODO: booleans! throw new Error(`Error converting pgSetting: ${typeof pgSetting} needs to be of type string, number or boolean.`); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aFBvc3RHcmFwaGlsZUNvbnRleHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9zdGdyYXBoaWxlL3dpdGhQb3N0R3JhcGhpbGVDb250ZXh0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHdDQUF5QztBQUN6QyxvQ0FBcUM7QUFFckMscUNBQXlFO0FBQ3pFLCtCQUErQjtBQUMvQixtRkFBdUU7QUFDdkUsNkNBQXFEO0FBRXJELHlEQUEwRDtBQUUxRCxNQUFNLGdCQUFnQixHQUFHLENBQ3ZCLENBQTRDLEVBQ1UsRUFBRSxDQUN4RCxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQVl2RCxNQUFNLE9BQU8sR0FBRyxjQUFjLENBQUMsdUJBQXVCLENBQUMsQ0FBQztBQUN4RCxNQUFNLFlBQVksR0FBRyxjQUFjLENBQUMsNkJBQTZCLENBQUMsQ0FBQztBQUNuRSxNQUFNLGFBQWEsR0FBRyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztBQUVyRTs7R0FFRztBQUNILFNBQVMsa0JBQWtCLENBQUMsT0FBaUMsRUFBRSxNQUFnQjtJQUM3RSxPQUFPLENBQ0wsY0FBYyxFQUNkLE1BQU0sQ0FBQyxRQUFRLElBQUksT0FBTyxFQUMxQixNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUNyQyxNQUFNLENBQUMsT0FBTyxJQUFJLE1BQU0sRUFDeEIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsYUFBYSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFDL0MsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsWUFBWSxNQUFN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