summaryrefslogtreecommitdiff
path: root/gitaccess
diff options
context:
space:
mode:
Diffstat (limited to 'gitaccess')
-rwxr-xr-xgitaccess157
1 files changed, 141 insertions, 16 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 ${@}

Generated by cgit