diff options
author | Daniel Silverstone <dsilvers@digital-scurf.org> | 2017-07-08 15:43:56 +0100 |
---|---|---|
committer | Daniel Silverstone <dsilvers@digital-scurf.org> | 2017-07-08 15:43:56 +0100 |
commit | c3b09524a73fd362d5ac1c2080639ad95b0456b2 (patch) | |
tree | 57007f01b492c490574119ccd95efff1788d1fdf /lib | |
parent | 5d81846da0d3f827235e2fdd209541e005dfafb7 (diff) | |
parent | aedd1964a467e4f9aaca49ca5fa2bb40a81e1ca8 (diff) | |
download | gitano-c3b09524a73fd362d5ac1c2080639ad95b0456b2.tar.gz |
Merge branch 'dsilvers/hooks'
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitano.lua | 7 | ||||
-rw-r--r-- | lib/gitano/auth.lua | 18 | ||||
-rw-r--r-- | lib/gitano/hooks.lua | 137 |
3 files changed, 161 insertions, 1 deletions
diff --git a/lib/gitano.lua b/lib/gitano.lua index 38c22ef..5354a0f 100644 --- a/lib/gitano.lua +++ b/lib/gitano.lua @@ -41,9 +41,13 @@ local supple = require 'gitano.supple' local auth = require 'gitano.auth' local plugins = require 'gitano.plugins' local i18n = require 'gitano.i18n' +<<<<<<< HEAD local patterns = require 'gitano.patterns' +======= +local hooks = require 'gitano.hooks' +>>>>>>> dsilvers/hooks -local _VERSION = {1, 0, 0} +local _VERSION = {1, 1, 0} _VERSION.major = _VERSION[1] _VERSION.minor = _VERSION[2] _VERSION.patch = _VERSION[3] @@ -70,4 +74,5 @@ return { plugins = plugins, i18n = i18n, patterns = patterns, + hooks = hooks, } diff --git a/lib/gitano/auth.lua b/lib/gitano/auth.lua index c5a1095..bf3260f 100644 --- a/lib/gitano/auth.lua +++ b/lib/gitano/auth.lua @@ -37,6 +37,7 @@ local log = require 'gitano.log' local repository = require 'gitano.repository' local util = require 'gitano.util' local i18n = require 'gitano.i18n' +local hooks = require 'gitano.hooks' local gall = require 'gall' local luxio = require 'luxio' @@ -121,6 +122,23 @@ local function is_authorized(user, source, cmdline, repo_root, i18n.expand("CLIENT_CONNECTED", { ip=ip, user=user, key=keytag, cmdline=cmdline})) + local cancel + cancel, ip, user, keytag, parsed_cmdline = + (function(c,i,u,k,...) + return c, i, u, k, {...} + end)(hooks.run(hooks.names.PREAUTH_CMDLINE, false, + ip, user, keytag, unpack(parsed_cmdline))) + + if cancel == nil then + log.syslog.err(i18n.expand("PREAUTH_CMDLINE_HOOK_ABORTED", {reason=ip})) + log.critical(i18n.expand("PREAUTH_CMDLINE_HOOK_DECLINED", {reason=ip})) + return nil + end + if cancel then + log.critical(i18n.expand("PREAUTH_CMDLINE_HOOK_DECLINED", {reason=ip})) + return nil + end + local cmd = command.get(parsed_cmdline[1]) if not cmd then diff --git a/lib/gitano/hooks.lua b/lib/gitano/hooks.lua new file mode 100644 index 0000000..0c3cba9 --- /dev/null +++ b/lib/gitano/hooks.lua @@ -0,0 +1,137 @@ +-- gitano.hooks +-- +-- Hook management routines for Gitano +-- +-- Copyright 2017 Daniel Silverstone <dsilvers@digital-scurf.org> +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- 3. Neither the name of the author nor the names of their contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. +-- +-- +-- + +local hooks = {} + +-- In order to centralise and ensure hook names are good, these +-- are the names of the hooks Gitano uses internally. +-- Plugins are at liberty to add their own hooks if they want, but +-- Gitano is unlikely to call them itself. + +local hook_names = { + -- Called by gitano.auth.is_authorized to allow manipulation of the + -- command line if wanted. Hook functions should take the form: + -- function preauth_cmdline_hook(cancel, config, ip, user, keytag, ...) + -- -- Decide what to do, if we're rejecting the command then set cancel + -- -- to something truthy such as: + -- return "stop", true, "Some reason" + -- -- otherwise try + -- return "continue" + -- -- or + -- return "update", cancel, config, ip, user, keytag, ... + -- end + PREAUTH_CMDLINE = "PREAUTH_CMDLINE", + -- Called by gitano-post-receive to allow processing to occur on the git + -- post-receive event if needed. The hook carries all the usual functions + -- as well as any registered by plugins. + -- Core admin stuff (running gitano-admin updates, update-server-info, etc) + -- runs at -1000. The supple hooks run at 0. Hook functions take the form: + -- function post_receive_hook(repo, updates) + -- -- Decide what to do. If we want to stop the hooks, return "stop" + -- -- but only do that if we MUST, since it will alter expected behaviour. + -- return "stop" + -- -- Otherwise, normally we'd just continue + -- return "continue" + -- -- Finally we can update if we want to alter the updates table + -- return "update", repo, different_updates + -- end + POST_RECEIVE = "POST_RECEIVE", +} + +local function _get_hook(hookname) + local ret = hooks[hookname] or {} + hooks[hookname] = ret + return ret +end + +local function _sort(hooktab) + table.sort(hooktab, function(a,b) return a[1] < b[1] end) +end + +local function add_to_hook(hookname, priority, func) + assert(hookname, "Cannot add to a nil hook") + assert(type(priority) == "number", "Cannot use a non-numerical priority") + assert(type(func) == "function", "Cannot use a non-function hook func") + local h = _get_hook(hookname) + h[#h+1] = {priority, func} +end + +local function _allbutone(_, ...) + return ... +end + +local function run_hook(hookname, ...) + assert(hookname, "Cannot run a nil hook") + local h = _get_hook(hookname) + _sort(h) + local args = {...} + for _, entry in ipairs(h) do + local result = { entry[2](unpack(args)) } + if result[1] == nil then + return unpack(result) + elseif type(result[1]) ~= "string" then + return nil, "Bad results", unpack(result) + elseif result[1] == "stop" then + return _allbutone(unpack(result)) + elseif result[1] == "update" then + args = {_allbutone(unpack(result))} + elseif result[1] == "continue" then + -- Nothing to do + else + return nil, "Bad results", unpack(result) + end + end + return unpack(args) +end + +-- Hook functions take the form: +-- action, ... = hookfunc(...) +-- where the ... is chained through, and returned verbatim at +-- the end. +-- action can be nil (on error) or else one of: +-- continue --> call the next hook function if there is one (not chaining ...) +-- update --> as for 'continue' but chaining the ... +-- stop --> stop now and return the rest of the results. + +-- Wherever Gitano registers a hook for something, the hook priority +-- of zero will be Gitano's action. So if you want to alter what is +-- passed to Gitano's default behaviour, register with a negative value +-- and if you want to just do more afterwards, register with a positive +-- value + +return { + add = add_to_hook, + run = run_hook, + names = hook_names, +} |