-
Notifications
You must be signed in to change notification settings - Fork 1
Add ability to do a deep lookup into a JSON tree #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
NathanAhlstrom
wants to merge
5
commits into
kushalpandya:master
Choose a base branch
from
NathanAhlstrom:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
7830892
added file lookup.js to include underscore and deep JSON map lookup c…
NathanAhlstrom cb9ec5f
updated config.js to generalize
NathanAhlstrom bba449f
minor update to shellscript.sh to echo current working directory.
NathanAhlstrom 9c76415
minor tweak to .gitignore to skip over VI swp files.
NathanAhlstrom 3cc5745
massive updates to GWServer.js: new functions (fnValidateConfig, fnCh…
NathanAhlstrom File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| # temporary VI files | ||
| .*.swp | ||
|
|
||
| # Logs | ||
| logs | ||
| *.log | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,115 +12,217 @@ | |
| * Main Server Script. | ||
| */ | ||
|
|
||
| var config = require("./config.json"), | ||
| os = require("os"), | ||
| http = require("http"), | ||
| proc = require("child_process"), | ||
| port = config.listenerPort, | ||
| supportedHooks = Object.keys(config.hooks), | ||
| fnProcessRequest, | ||
| fnVerifyMatches, | ||
| server; | ||
| var config = require("./config.js"), | ||
| _ = require("./lookup.js"), | ||
| os = require("os"), | ||
| fs = require("fs"), | ||
| path = require("path"), | ||
| http = require("http"), | ||
| proc = require("child_process"), | ||
| port = config.listenerPort, | ||
| host = (config.hasOwnProperty("hostname") && config.hostname != null) ? config.hostname : os.hostname(), | ||
| supportedHooks = Object.keys(config.hooks), | ||
| fnProcessRequest, | ||
| fnVerifyMatches, | ||
| server; | ||
|
|
||
|
|
||
| /** | ||
| * This method walks through the config.hooks to validate correct | ||
| * execute permission on the command line scripts. | ||
| */ | ||
| fnValidateConfig = function(sH) { | ||
| for (var i = 0; i < sH.length; i++) { | ||
| // read properties from config.js | ||
| file = _.lookup(config.hooks, sH[i] +".commandBatch"); | ||
| loc = _.lookup(config.hooks, sH[i] + ".commandDir"); | ||
|
|
||
| // gynmastics to create a file path | ||
| loc = loc == null ? "" : loc; | ||
| file = path.join(loc, file); | ||
|
|
||
| // check if we can execute this file. | ||
| try { | ||
| fs.accessSync(file, | ||
| fs.constants.F_OK | fs.constants.R_OK | | ||
| fs.constants.X_OK); | ||
| } catch (e) { | ||
| console.log("Missing file or execute permissions on file: " + file); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
| console.log("Recommend running chmod +x " + file); | ||
| // exiting hard with a big bad error | ||
| process.exit(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| /** | ||
| * This method verifies conditions given in config[<hook_type>].matches against requestBody | ||
| */ | ||
| fnVerifyMatches = function(requestBody, matchesCollection) { | ||
| var matchItem; | ||
|
|
||
| for (matchItem in matchesCollection) | ||
| { | ||
| if (!requestBody.hasOwnProperty(matchItem)) | ||
| return false; | ||
| else | ||
| { | ||
| if (requestBody[matchItem] === matchesCollection[matchItem]) | ||
| continue; | ||
| else | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| var matchItem; | ||
|
|
||
| for (matchItem in matchesCollection) | ||
| { | ||
| console.log("match: " + matchItem + | ||
| " value: " + _.lookup(requestBody, matchItem) + | ||
| " compare to: " + matchesCollection[matchItem]); | ||
|
|
||
| if (matchesCollection[matchItem] === _.lookup(requestBody,matchItem) ) | ||
| continue; | ||
| else | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| }; | ||
|
|
||
|
|
||
| fnCheckRequest = function(reqHeaders, type) { | ||
|
|
||
| var token, secretKey; | ||
|
|
||
| if ( reqHeaders.hasOwnProperty('x-gitlab-token') ) { | ||
| token = reqHeaders['x-gitlab-token']; | ||
| } | ||
| else { | ||
| return false; | ||
| } | ||
|
|
||
| if (reqHeaders.hasOwnProperty('x-gitlab-event') && | ||
| supportedHooks.indexOf(type) > -1) | ||
| { | ||
| secretKey = config.hooks[type]["secretKey"]; | ||
| } | ||
| else { | ||
| return false; | ||
| } | ||
|
|
||
| if ( token === secretKey ) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can just do |
||
| //console.info("token = %s, secretKey = %s", token, secretKey); | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * This method does all the processing and command execution on requestBody. | ||
| */ | ||
| fnProcessRequest = function(requestBody) { | ||
| var object_kind = requestBody.object_kind, | ||
| satisfiesMatches = false, | ||
| pipedOutput = [], | ||
| errors = [], | ||
| commandBatch, | ||
| hookConfig, | ||
| i; | ||
|
|
||
| hookConfig = config.hooks[object_kind]; | ||
| if (typeof hookConfig.matches === "object") // Check if 'matches' map is provided with this hook type. | ||
| satisfiesMatches = fnVerifyMatches(requestBody, hookConfig.matches); // Verify matches. | ||
| else | ||
| satisfiesMatches = true; | ||
|
|
||
| // Run commandBatch only if matches are satisfied. | ||
| if (satisfiesMatches) | ||
| { | ||
| // Beware, this is DANGEROUS. | ||
| commandBatch = proc.spawn(hookConfig.commandBatch); | ||
|
|
||
| // Collect Bash output. | ||
| commandBatch.stdout.on('data', function(data) { | ||
| pipedOutput.push(data); | ||
| }); | ||
|
|
||
| // Collect Bash errors. | ||
| commandBatch.stderr.on('data', function(data) { | ||
| errors.push(data); | ||
| }); | ||
|
|
||
| // Listen for end of commandBatch execution. | ||
| commandBatch.on('exit', function(status) { | ||
| if (status === 0) // Check if execution failed with non-Zero status | ||
| console.log(Buffer.concat(pipedOutput).toString()); // All good. | ||
| else | ||
| console.error('Hook Execution Terminated with status : %s \n', status, Buffer.concat(errors).toString()); | ||
| }); | ||
| } | ||
| var object_kind = requestBody.object_kind, | ||
| satisfiesMatches = false, | ||
| pipedOutput = [], | ||
| errors = [], | ||
| commandBatch, | ||
| hookConfig, | ||
| i; | ||
|
|
||
| // retrieve the configuration for this hook-type. | ||
| hookConfig = config.hooks[object_kind]; | ||
|
|
||
| // Check if 'matches' map is provided with this hook type. | ||
| if (typeof hookConfig.matches === "object") { | ||
| // Verify matches - run comparisons. | ||
| satisfiesMatches = fnVerifyMatches(requestBody, hookConfig.matches); | ||
| } | ||
| else { | ||
| // no "matches" map in config.js; skip comparison; satisfies = true | ||
| satisfiesMatches = true; | ||
| } | ||
|
|
||
| console.info("match %s at %s", satisfiesMatches, new Date()); | ||
|
|
||
| // Run commandBatch only if matches are satisfied. | ||
| if (satisfiesMatches) | ||
| { | ||
| console.info("running %s at %s", | ||
| hookConfig.commandBatch, new Date()); | ||
|
|
||
| options = { | ||
| "cwd": _.lookup(hookConfig, "commandDir") | ||
| }; | ||
|
|
||
| // Beware, this is DANGEROUS. | ||
| commandBatch = proc.spawn(hookConfig.commandBatch, | ||
| options); | ||
|
|
||
| // Collect command output. | ||
| commandBatch.stdout.on('data', function(data) { | ||
| pipedOutput.push(data); | ||
| }); | ||
|
|
||
| // Collect command errors. | ||
| commandBatch.stderr.on('data', function(data) { | ||
| errors.push(data); | ||
| }); | ||
|
|
||
| // Listen for end of commandBatch execution. | ||
| commandBatch.on('exit', function(status) { | ||
| if (status === 0) // Check if execution failed with non-Zero status | ||
| console.log(Buffer.concat(pipedOutput).toString()); // All good. | ||
| else | ||
| console.error('Hook Execution Terminated with status : %s \n', status, Buffer.concat(errors).toString()); | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| server = http.createServer(function(request, response) { | ||
| var reqHeaders = request.headers, | ||
| reqBody = []; | ||
|
|
||
| request | ||
| .on('data', function(chunk) { | ||
| reqBody.push(chunk); | ||
| }) | ||
| .on('end', function() { | ||
| reqBody = JSON.parse(Buffer.concat(reqBody).toString()); | ||
|
|
||
| // Check if | ||
| // x-gitlab-event header is present in headers AND | ||
| // object_kind is one of the supported hooks in config | ||
| // then respond accordingly. | ||
| if (reqHeaders.hasOwnProperty('x-gitlab-event') && | ||
| supportedHooks.indexOf(reqBody.object_kind) > -1) | ||
| { | ||
| response.statusCode = 200; | ||
| fnProcessRequest(reqBody); | ||
| } | ||
| else | ||
| response.statusCode = 400; | ||
|
|
||
| response.end(); | ||
| }); | ||
| var reqHeaders = request.headers, | ||
| reqBody = []; | ||
|
|
||
| //console.log(request.method); | ||
| if (request.method == 'GET') { | ||
| response.statusCode = 404; | ||
| response.end(); | ||
| return; | ||
| } | ||
|
|
||
| request | ||
| .on('data', function(chunk) { | ||
| reqBody.push(chunk); | ||
| }) | ||
| .on('end', function() { | ||
| reqBody = JSON.parse(Buffer.concat(reqBody).toString()); | ||
|
|
||
| // | ||
| // Check if | ||
| // x-gitlab-event header is present in headers | ||
| // AND object_kind is one of the supported hooks in config.js | ||
| // AND x-gitlab-token matches the secretKey in config.js | ||
| // then respond accordingly. | ||
| // | ||
| if ( fnCheckRequest(reqHeaders, reqBody.object_kind) ) | ||
| { | ||
| console.info("fnCheckRequest passed"); | ||
| response.statusCode = 200; | ||
| fnProcessRequest(reqBody); | ||
| } | ||
| else { | ||
| console.info("fnCheckRequest failed"); | ||
| response.statusCode = 400; | ||
| } | ||
|
|
||
| response.end(); | ||
| }); | ||
| }); | ||
|
|
||
| server.listen(port, function() { | ||
| console.info("%s started on %s:%d at %s", | ||
| config.serverTitle, | ||
| os.hostname(), | ||
| port, | ||
| new Date() | ||
| ); | ||
|
|
||
| /* | ||
| * function to look through config.js to verify execute permissions | ||
| * on all "commandBatch" files. | ||
| */ | ||
| fnValidateConfig(supportedHooks); | ||
|
|
||
|
|
||
| /* | ||
| * begin nodejs httpd server listening on specified ports/hostnames. | ||
| */ | ||
| server.listen(port, host, function() { | ||
| console.info("%s started on %s:%d at %s", | ||
| config.serverTitle, | ||
| host, | ||
| port, | ||
| new Date() | ||
| ); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // | ||
| // renamed this config.json file to config.js to be able to introduce comments. | ||
| // required the same in nodejs. require('config'); | ||
| // only change is adding "module.exports = " to beginning of file. | ||
| // | ||
| module.exports = { | ||
| "serverTitle": "gitlab webhook listener", | ||
| "hostname": "localhost", // remove for os.hostname() | ||
| "listenerPort": 9000, | ||
| "hooks": { | ||
| "push": { | ||
| "secretKey": "SECRETKEYHERE", | ||
| "matches": { | ||
| "project.default_branch": "production", | ||
| "project.path_with_namespace": "username/reponame" | ||
| }, | ||
| "commandBatch": "./shellscript.sh" | ||
| }, | ||
|
|
||
| /* | ||
| * unused | ||
| * | ||
| "tag_push": { | ||
|
|
||
| }, | ||
| "issue": { | ||
|
|
||
| }, | ||
| "note": { | ||
|
|
||
| }, | ||
| "merge_request": { | ||
|
|
||
| } | ||
| * | ||
| */ | ||
|
|
||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * lookup function for inspection objects for keys down several levels. | ||
| * from: | ||
| * https://gist.github.com/megawac/6162481#file-underscore-lookup-js | ||
| */ | ||
|
|
||
| var _ = require('underscore'); | ||
|
|
||
| _.mixin({ | ||
| lookup: function (obj, key) { | ||
| var type = typeof key, i = 0, length; | ||
| if (type == "string" || type == "number") { | ||
| key = ("" + key).replace(/\[(.*?)\]/g, function (m, key) { //handle case where [1] or ['xa'] may occur | ||
| return "." + key.replace(/^["']|["']$/g, ""); //strip quotes at the start or end of the key | ||
| }).split("."); | ||
| } | ||
| for (length = key.length; i < length; i++) { | ||
| if (_.has(obj, key[i])) obj = obj[key[i]]; | ||
| else return void 0; | ||
| } | ||
| return obj; | ||
| } | ||
| }); | ||
|
|
||
| module.exports = _; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this file for the most part is ES5 code, but we can change this loop to
sH.forEachand then also use template strings within_.lookupfor cleaner string concatenation. What do you think? 🙂