diff options
Diffstat (limited to 'gitaccess')
-rwxr-xr-x | gitaccess | 157 |
1 files changed, 141 insertions, 16 deletions
@@ -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 ${@} |