-- gitano.auth -- -- Gitano entry point for unified authorisation/authentication -- -- Copyright 2014 Richard Ipsum -- Copyright 2016-2017 Daniel Silverstone -- 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 config = require 'gitano.config' local command = require 'gitano.command' 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' local function load_admin_conf(repo_root) local admin_repo = gall.repository.new((repo_root or "") .. "/gitano-admin.git") if not admin_repo then log.critical(i18n.expand("ERROR_CANNOT_FIND_ADMIN_REPO")) return nil end local admin_head = admin_repo:get(admin_repo.HEAD) if not admin_head then log.critical(i18n.expand("ERROR_BAD_ADMIN_REPO")) return nil end local admin_conf, msg = config.parse(admin_head) if not admin_conf then log.critical(i18n.expand("ERROR_CANNOT_PARSE_ADMIN")) log.critical(" * " .. (msg or "No error?")) return nil end return admin_conf end local function set_environment(repo_root, repo, context, transactionid) local env = { ["GITANO_ROOT"] = repo_root, ["GITANO_USER"] = context.user, ["GITANO_KEYTAG"] = context.keytag, ["GITANO_PROJECT"] = (repo or {}).name or "", ["GITANO_SOURCE"] = context.source, ["GITANO_TRANSACTION_ID"] = transactionid, ["GITANO_RUNNING"] = "yes", } for k, v in pairs(env) do luxio.setenv(k, v) end return env end local function is_authorized(user, source, cmdline, repo_root, transactionid, start_log_level, keytag) local authorized = false config.repo_path(repo_root) if not user or not cmdline then return nil end local parsed_cmdline, warnings = util.parse_cmdline(cmdline) if (#warnings > 0) then log.error(i18n.expand("ERROR_PARSING_COMMAND")) return nil end local admin_conf = load_admin_conf(repo_root) if admin_conf == nil then log.fatal(i18n.expand("ERROR_COULD_NOT_LOAD_CONFIG")) end if admin_conf.groups["gitano-admin"].filtered_members[user] then log.set_level(start_log_level) end if not admin_conf.global.silent then log.bump_level(log.level.CHAT) end ip = os.getenv("REMOTE_ADDR") or "unknown ip" log.syslog.info( 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 log.critical(i18n.expand("ERROR_UNKNOWN_COMMAND", {cmd=parsed_cmdline[1]})) return nil end local repo if cmd.takes_repo and #parsed_cmdline >= 1 then repo, parsed_cmdline = cmd.detect_repo(admin_conf, parsed_cmdline) if not repo and not parsed_cmdline then return nil end end if user == "gitano-bypass" then log.state(i18n.expand("BYPASS_USER_BANNER_HEADER")) log.state(i18n.expand("BYPASS_USER_ALERT_MESSAGE")) log.state(i18n.expand("BYPASS_USER_BANNER_FOOTER")) end if not cmd.validate(admin_conf, repo, parsed_cmdline) then log.critical(i18n.expand("ERROR_VALIDATION_FAILED")) return nil end local context = {source = source, user = user, keytag = keytag} local action, reason = cmd.prep(admin_conf, repo, parsed_cmdline, context) if not action then log.critical(reason) log.critical(i18n.expand("ERROR_RULESET_UNCLEAN")) return nil end local env if action == "allow" then log.info(reason or i18n.expand("RULESET_ALLOWED")) authorized = true env = set_environment(repo_root, repo, context, transactionid) else log.critical(reason) log.critical(i18n.expand("RULESET_DENIED")) end return authorized, cmd, parsed_cmdline, admin_conf, env, repo end return { is_authorized = is_authorized }