summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2017-07-08 15:43:56 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2017-07-08 15:43:56 +0100
commitc3b09524a73fd362d5ac1c2080639ad95b0456b2 (patch)
tree57007f01b492c490574119ccd95efff1788d1fdf /lib
parent5d81846da0d3f827235e2fdd209541e005dfafb7 (diff)
parentaedd1964a467e4f9aaca49ca5fa2bb40a81e1ca8 (diff)
downloadgitano-c3b09524a73fd362d5ac1c2080639ad95b0456b2.tar.gz
Merge branch 'dsilvers/hooks'
Diffstat (limited to 'lib')
-rw-r--r--lib/gitano.lua7
-rw-r--r--lib/gitano/auth.lua18
-rw-r--r--lib/gitano/hooks.lua137
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,
+}