From 2561987d205526d963494221b46b0e4f0d5739dd Mon Sep 17 00:00:00 2001 From: Tyler Brock Date: Thu, 26 May 2016 11:17:24 -0700 Subject: [PATCH] Add webhookKey support (#1920) --- spec/ParseHooks.spec.js | 59 ++++++++++++++++++++++++++++-- spec/helper.js | 1 + src/Controllers/HooksController.js | 14 +++++-- src/ParseServer.js | 4 +- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index d3c04f6900..1216c56005 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -17,7 +17,7 @@ app.listen(12345); describe('Hooks', () => { - it("should have some hooks registered", (done) => { + it("should have no hooks registered", (done) => { Parse.Hooks.getFunctions().then((res) => { expect(res.constructor).toBe(Array.prototype.constructor); done(); @@ -27,7 +27,7 @@ describe('Hooks', () => { }); }); - it("should have some triggers registered", (done) => { + it("should have no triggers registered", (done) => { Parse.Hooks.getTriggers().then( (res) => { expect(res.constructor).toBe(Array.prototype.constructor); done(); @@ -291,7 +291,7 @@ describe('Hooks', () => { console.error(err); fail("Should not fail calling a function"); done(); - }) + }); }); it("should run the function on the test server", (done) => { @@ -299,7 +299,7 @@ describe('Hooks', () => { app.post("/SomeFunctionError", function(req, res) { res.json({error: {code: 1337, error: "hacking that one!"}}); }); - // The function is delete as the DB is dropped between calls + // The function is deleted as the DB is dropped between calls Parse.Hooks.createFunction("SOME_TEST_FUNCTION", hookServerURL+"/SomeFunctionError").then(function(){ return Parse.Cloud.run("SOME_TEST_FUNCTION") }, (err) => { @@ -317,6 +317,57 @@ describe('Hooks', () => { }); }); + it("should provide X-Parse-Webhook-Key when defined", (done) => { + app.post("/ExpectingKey", function(req, res) { + if (req.get('X-Parse-Webhook-Key') === 'hook') { + res.json({success: "correct key provided"}); + } else { + res.json({error: "incorrect key provided"}); + } + }); + + Parse.Hooks.createFunction("SOME_TEST_FUNCTION", hookServerURL+"/ExpectingKey").then(function(){ + return Parse.Cloud.run("SOME_TEST_FUNCTION") + }, (err) => { + console.error(err); + fail("Should not fail creating a function"); + done(); + }).then(function(res){ + expect(res).toBe("correct key provided"); + done(); + }, (err) => { + console.error(err); + fail("Should not fail calling a function"); + done(); + }); + }); + + it("should not pass X-Parse-Webhook-Key if not provided", (done) => { + setServerConfiguration(Object.assign({}, defaultConfiguration, { webhookKey: undefined })); + app.post("/ExpectingKeyAlso", function(req, res) { + if (req.get('X-Parse-Webhook-Key') === 'hook') { + res.json({success: "correct key provided"}); + } else { + res.json({error: "incorrect key provided"}); + } + }); + + Parse.Hooks.createFunction("SOME_TEST_FUNCTION", hookServerURL+"/ExpectingKeyAlso").then(function(){ + return Parse.Cloud.run("SOME_TEST_FUNCTION") + }, (err) => { + console.error(err); + fail("Should not fail creating a function"); + done(); + }).then(function(res){ + fail("Should not succeed calling that function"); + done(); + }, (err) => { + expect(err.code).toBe(141); + expect(err.message).toEqual("incorrect key provided"); + done(); + }); + }); + it("should run the beforeSave hook on the test server", (done) => { var triggerCount = 0; diff --git a/spec/helper.js b/spec/helper.js index 5d804d12e9..8e36b20732 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -23,6 +23,7 @@ var defaultConfiguration = { dotNetKey: 'windows', clientKey: 'client', restAPIKey: 'rest', + webhookKey: 'hook', masterKey: 'test', collectionPrefix: 'test_', fileKey: 'test', diff --git a/src/Controllers/HooksController.js b/src/Controllers/HooksController.js index 0c4ae4f794..73cdf17272 100644 --- a/src/Controllers/HooksController.js +++ b/src/Controllers/HooksController.js @@ -4,6 +4,7 @@ import * as DatabaseAdapter from "../DatabaseAdapter"; import * as triggers from "../triggers"; import * as Parse from "parse/node"; import * as request from "request"; +import { logger } from '../logger'; const DefaultHooksCollectionName = "_Hooks"; @@ -12,9 +13,10 @@ export class HooksController { _collectionPrefix:string; _collection; - constructor(applicationId:string, collectionPrefix:string = '') { + constructor(applicationId:string, collectionPrefix:string = '', webhookKey) { this._applicationId = applicationId; this._collectionPrefix = collectionPrefix; + this._webhookKey = webhookKey; this.database = DatabaseAdapter.getDatabaseConnection(this._applicationId, this._collectionPrefix).WithoutValidation(); } @@ -79,7 +81,7 @@ export class HooksController { } addHookToTriggers(hook) { - var wrappedFunction = wrapToHTTPRequest(hook); + var wrappedFunction = wrapToHTTPRequest(hook, this._webhookKey); wrappedFunction.url = hook.url; if (hook.className) { triggers.addTrigger(hook.triggerName, hook.className, wrappedFunction, this._applicationId) @@ -153,7 +155,7 @@ export class HooksController { }; } -function wrapToHTTPRequest(hook) { +function wrapToHTTPRequest(hook, key) { return (req, res) => { let jsonBody = {}; for (var i in req) { @@ -174,6 +176,12 @@ function wrapToHTTPRequest(hook) { body: JSON.stringify(jsonBody) }; + if (key) { + jsonRequest.headers['X-Parse-Webhook-Key'] = key; + } else { + logger.warn('Making outgoing webhook request without webhookKey being set!'); + } + request.post(hook.url, jsonRequest, function (err, httpResponse, body) { var result; if (body) { diff --git a/src/ParseServer.js b/src/ParseServer.js index 85b2b40c02..bf746facad 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -98,6 +98,7 @@ class ParseServer { javascriptKey, dotNetKey, restAPIKey, + webhookKey, fileKey = 'invalid-file-key', facebookAppIds = [], enableAnonymousUsers = true, @@ -166,7 +167,7 @@ class ParseServer { const filesController = new FilesController(filesControllerAdapter, appId); const pushController = new PushController(pushControllerAdapter, appId); const loggerController = new LoggerController(loggerControllerAdapter, appId); - const hooksController = new HooksController(appId, collectionPrefix); + const hooksController = new HooksController(appId, collectionPrefix, webhookKey); const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails }); const liveQueryController = new LiveQueryController(liveQuery); const cacheController = new CacheController(cacheControllerAdapter, appId); @@ -179,6 +180,7 @@ class ParseServer { javascriptKey: javascriptKey, dotNetKey: dotNetKey, restAPIKey: restAPIKey, + webhookKey: webhookKey, fileKey: fileKey, facebookAppIds: facebookAppIds, cacheController: cacheController,