summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgitaccess157
-rwxr-xr-xgitaccess.pre-receive74
2 files changed, 141 insertions, 90 deletions
diff --git a/gitaccess b/gitaccess
index 7e24c4e..bc72e03 100755
--- a/gitaccess
+++ b/gitaccess
@@ -21,28 +21,153 @@
# Description
# -----------
#
-# This script is intended solely to read variables passed to it and set them up
-# as environmental variables for later use.
+# This script provides basic access controls to git repos. For this script to
+# work, it requires that each repository has a 'users' file. This script reads
+# that file and determines if the user associated with the logged in ssh key
+# has access to that repo.
#
-# This script should be called by ~/.ssh/authorized keys using the following syntax
+# This script also provides support for interactive git shell, interactive
+# shell rejection via the no-interactive-shell script, and any other scripts
+# that are placed inside the ~/git-shell-commands directory.
+#
+# To use this script for a specified ssh key, call it using the command
+# directive in the ~/.ssh/authorized keys file using the following syntax
#
# # Key for user <username>
-# command="~/bin/gitaccess <username>" ecdsa-sha2-nistp521 AAAAE2v....
+# command="gitaccess <username>" ecdsa-sha2-nistp521 AAAAE2v....
+#
+
+
+#
+# Logging function for standardized log output. This also ensures that log
+# messages don't cause perceived corruption in git responses, as they always
+# report to stderr.
+#
+# @param lvl Log level string. Can be anything, but error, info, warn, fatal,
+# etc. are recomended
+# @param msg Log message
+#
+log() {
+ local lvl=${1}
+ shift
+ local msg=${@}
+
+ d=$(date '+%F %T')
+ printf "%s %s %s\n" "${d}" "${lvl}" "${msg[@]}" >> ~/git.log
+ printf "%s %s %s\n" "${d}" "${lvl}" "${msg[@]}" >&2
+}
+
+# Some logging macros to save typing time and space
+lerror() { log 'error' ${@}; }
+linfo() { log 'info ' ${@}; }
+lwarn() { log 'warn ' ${@}; }
+lwarn() { log 'fatal' ${@}; }
+
+
+#
+# Resolves the specified git repo path. Returns failure if...
+# No repo exists at the path
+# No repo exists at the path with .git appended
+# The specified path exists, but is no a bare repository
+#
+# If the path is able to be resolved, the updated path is returned with an exit
+# code of 0.
+#
+# @param repopath Path to the repo to resolve
#
+git_resolve_path() {
+ local repopath=${1:-}
+ local isbare
+
+ # Resolve the path correctly if it has .git on the end that was not specified
+ [ -d ${repopath}.git ] && repopath=${repopath}.git
+ # If no repo can be found still, return failure
+ [ ! -d ${repopath} ] && lerror "No repo exists at ${repopath}" && return 1
+
+ isbare=$(git --git-dir="${repopath}" rev-parse --is-bare-repository 2>/dev/null)
+ [ "${isbare:-}" = 'true' ] && printf ${repopath} && return 0
+
+ lerror "Not a git repository: ${repopath}"
+ return 1
+}
+
+
+#
+# Checks if the specified user has acccess to the specified repo.
+# Requires the presence of the users file at the top level of the bare repo.
+# This file should contain one username per line
+#
+# @param repopath Path to the repo we're checking access to
+# @param user Username to check for access
+#
+git_check_access() {
+ local repopath=${1}
+ local user=${2}
+
+ local found=0 # Number of times the user is found in the users file
+
+ if [ -d ${repopath} ]; then
+ # Fail if users file is not found
+ if [ ! -f ${repopath}/users ]; then
+ lerror "No users file found in ${repopath}."
+ lerror "Access denied"
+ printf 0
+ return 1
+ fi
+
+ # Check if the user is in the users file
+ found=$(grep -c "^[ ]*${user}[ ]*$" ${repopath}/users)
+ if [ ${found} -eq 0 ]; then
+ lerror "Permission denied for ${user} to ${repopath}"
+ printf 0
+ else
+ linfo "Permission granted for ${user} to ${repopath}"
+ printf 1
+ fi
+ else
+ lerror "Could not find repo at ${repopath}."
+ return 3
+ fi
+}
+
+
+#
+# Ye olde main
+#
+main() {
+ local user="${1:-}"
+
+ local repopath='none'
-# Detect if someone tries to launch this script from this script, thus creating
-# an infinite recursive loop spawning subshells.
-if [ "${SSH_ORIGINAL_COMMAND:-}" == "$(basename ${0})" ]; then
- printf "ERROR: Blocking infinite recursion\n"
- exit 1
-fi
+ # Detect if someone tries to launch this script from this script, thus creating
+ # an infinite recursive loop spawning subshells.
+ if [ "${SSH_ORIGINAL_COMMAND:-}" = "$(basename ${0})" ]; then
+ log error "Blocking infinite recursion"
+ exit 1
+ fi
+ # Launch git interractive shell if no commands were specified
+ if [ -z "${SSH_ORIGINAL_COMMAND:-}" ]; then
+ /usr/bin/env git shell
+ return $?
+ fi
+
+ # If the user specified a git-* command, check if they have access to run it.
+ if [ "${SSH_ORIGINAL_COMMAND:0:4}" = 'git-' ]; then
+ # Parse the repo path out from the git command
+ repopath="$(echo ${SSH_ORIGINAL_COMMAND} | cut -d ' ' -f 2 | tr -d "'")"
-# All checks passed...
+ # Resolve the repo path (in case it needs a .git or something else)
+ repopath=$(git_resolve_path "${repopath}")
+ [ $? -gt 0 ] && return 1
-# The first argument should be the username, as defailed in the script
-# instructions
-export GIT_USER="${1}"
+ # Verify the user has access to this repo
+ allowed=$(git_check_access "${repopath}" "${user}")
+ [ ${allowed} -ne 1 ] && exit 1
+ fi
+
+ # All checks passed. Run the command
+ /usr/bin/env git shell -c "${SSH_ORIGINAL_COMMAND}"
+}
-# Environmental variables set up. Proceed as was originally planned.
-/usr/bin/env git shell -c "${SSH_ORIGINAL_COMMAND}"
+main ${@}
diff --git a/gitaccess.pre-receive b/gitaccess.pre-receive
deleted file mode 100755
index e91c59e..0000000
--- a/gitaccess.pre-receive
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env bash
-#
-# Gitaccess implements basic access controls for git.
-# Copyright (C) 2015 Aaron Ball <nullspoon@iohq.net>
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-
-#
-# Description
-# -----------
-#
-# This is the pre-receive hook which allows for user filtering to the current
-# working repo.
-#
-# Not that this REQUIRES the GIT_USER variable to be set. This is set by the
-# gitaccess script.
-#
-# This script reads in the user's name as passed by the GIT_USER variable, and
-# checks a users file (one user per line) for the given username. These
-# usernames are directly associated with ssh keys used to log in.
-#
-# If the user is found, git proceeds as normal with a "user permitted" message.
-#
-# If the user is not found in the users file, an error message is printed and
-# the git push is aborted.
-#
-
-# Log whatever we want to
-function log {
- d=$(date '+%s')
- echo "${d}: ${@}" >> ~/git.log
-}
-
-# Read STDIN pre-receive arguments
-read oldrev newrev refname
-
-# Determine the branch and repo names for better logging
-branch=$(basename "${refname}")
-repo=$(basename "$(pwd)")
-
-commit_dest_str="repo '${repo}', branch '${branch}'"
-
-# Check if user variable is set. Abort if it is not.
-if [[ -z ${GIT_USER} ]]; then
- echo "Error: Unknown ssh key. Rejecting push."
- exit 1
-fi
-
-log "Attempted login for user ${GIT_USER} on ${commit_dest_str}"
-
-# See if user is permitted access to this repo
-grep -v '^#' users | grep "^${GIT_USER}\$" 2>&1 1>/dev/null
-
-if [[ $? != 0 ]]; then
- log "User is not permitted access to repo $(pwd)"
- echo "Error: Permission denied for user ${GIT_USER} on ${commit_dest_str}."
- echo "Aborting."
- exit 1
-else
- echo "User ${GIT_USER} accepted for ${commit_dest_str}. Allowing push."
-fi

Generated by cgit