#!/bin/sh # # An example hook script to blocks unannotated tags from entering. # Called by git-receive-pack with arguments: refname sha1-old sha1-new # Merged with # http://www.kernel.org/pub/software/scm/git/docs/howto/update-hook-example.txt # by Andrei Pelinescu # # To enable this hook, make this file executable by "chmod +x update". # # Config # ------ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. # hooks.allowdeletetag # This boolean sets whether deleting tags will be allowed in the # repository. By default they won't be. # hooks.allowdeletebranch # This boolean sets whether deleting branches will be allowed in the # repository. By default they won't be. # hooks.mailNotification # This boolean set whether or not mail notification messages will be sent # mail.envelopeSender # mail.recipient # mail.gitwebURL # mail from # mail.emailMap # acl.defaultPolicy # If set to allow an no rules match in the allowed-users or allowed-group # file the commit will be allowed # acl.usernameBranches # If set to "allow" commits to refs/heads/${username}/.* are allowed # even if not specified in the allowed-users file # # Files # ------ # $GIT_DIR/info/allowed-users # file containing users allowed to commit on one branch. Each # line contains a branch pattern followed by a list of user patterns # (patterns are regular expressions, white space separated). # Empty lines and comments (line starting with '#' are allowed. # Rules are evaluated in-order and evaluation stops at first match. # Branch patterns on which non-fast-forward is allowed must start with a # '+'. # $GIT_DIR/info/allowed-groups # file containing groups allowed to commit on one branch (the same format # as above, but uses the system groups instead of the usernames). # allowed-groups is evaluated only if no match was found in allowed-users. # --- Command line refname="$1" oldrev="$2" newrev="$3" # If you are having trouble with this access control hook script # you can try setting this to true. It will tell you exactly # why a user is being allowed/denied access. # false|true verbose=false # --- Safety check if [ -z "$GIT_DIR" ]; then echo "Don't run this script from the command line." >&2 echo " (if you want, you could supply GIT_DIR then run" >&2 echo " $0 )" >&2 exit 1 fi if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then echo "Usage: $0 " >&2 exit 1 fi # --- Config allowunannotated=$(git config --bool hooks.allowunannotated) allowdeletebranch=$(git config --bool hooks.allowdeletebranch) allowdeletetag=$(git config --bool hooks.allowdeletetag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then echo "*** Project description file hasn't been set" >&2 exit 1 fi # Default shell globbing messes things up downstream GLOBIGNORE=* # functions used in the access control part (from # http://www.kernel.org/pub/software/scm/git/docs/howto/update-hook-example.txt # ) function grant { $verbose && echo >&2 "-Grant- $1" echo grant exit 0 } function deny { $verbose && echo >&2 "-Deny- $1" echo deny exit 1 } function info { $verbose && echo >&2 "-Info- $1" } # Mail - send commit mails # params: refname oldrev newrev function send_mail { mail=$(git config --bool hooks.mailNotification) if [ "$mail" == "true" ] ; then sender=$(git config mail.envelopeSender) recipient=$(git config mail.recipient) gitweb=$(git config mail.gitwebURL) from=$(git config mail.from) email_map=$(git config mail.emailMap) if [ "$sender" != "" ] ; then OPTS="$OPTS -f $sender"; fi if [ "$recipient" != "" ] ; then OPTS="$OPTS -m $recipient"; fi if [ "$gitweb" != "" ] ; then OPTS="$OPTS -u $gitweb" fi if [ "$from" != "" ] ; then OPTS="$OPTS -F '$from'" fi if [ "$email_map" != "" ] ; then OPTS="$OPTS -e "`eval echo $email_map` fi $GIT_DIR/hooks/mail $OPTS -- $1 $2 $3 fi } # if usernameBranches=="allow" and the branch maches the userbranch format # grant access function allow_userBranches { userbranches=$(git config acl.usernameBranches) info "userbranches = $userbranches" if [ "$userbranches" = "allow" ] then matchlen=$(expr "$refname" : ^refs/heads/"${username}"/.*) if [ "$matchlen" = ${#refname} ] then send_mail $refname $oldrev $newrev grant >/dev/null "Granting access to user branch" fi fi } # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then newrev_type=delete else newrev_type=$(git-cat-file -t $newrev) fi case "$refname","$newrev_type" in refs/tags/*,commit) # un-annotated tag short_refname=${refname##refs/tags/} if [ "$allowunannotated" != "true" ]; then echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 exit 1 fi ;; refs/tags/*,delete) # delete tag if [ "$allowdeletetag" != "true" ]; then echo "*** Deleting a tag is not allowed in this repository" >&2 exit 1 fi ;; refs/tags/*,tag) # annotated tag ;; refs/heads/*,commit) # branch # find out if it's a fast forward or not (needed for more detailed # access control) # No rebasing or rewinding if expr "$oldrev" : '0*$' >/dev/null; then info "The branch '$1' is new..." else # updating -- make sure it is a fast forward mb=$(git-merge-base "$oldrev" "$newrev") case "$mb,$oldrev" in "$oldrev,$mb") info "Update is fast-forward" ;; *) noff=y; info "This is not a fast-forward update.";; esac fi ;; refs/heads/*,delete) # delete branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/remotes/*,commit) # tracking branch ;; refs/remotes/*,delete) # delete tracking branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a tracking branch is not allowed in this repository" >&2 exit 1 fi ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 exit 1 ;; esac # # Access control # # Implement per-branch controls based on username allowed_users_file=$GIT_DIR/info/allowed-users username=$(id -u -n) info "The user is: '$username'" if test -f "$allowed_users_file" then rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' | while read heads user_patterns do # does this rule apply to us? # [ignore leading +, it is our modifier for allowing non-fast-forwards] head_pattern=${heads#+} matchlen=$(expr "$refname" : "${head_pattern#+}") test "$matchlen" = ${#refname} || continue # if non-ff, $heads must be with the '+' prefix test -n "$noff" && test "$head_pattern" = "$heads" && continue info "Found matching head pattern: '$head_pattern'" for user_pattern in $user_patterns; do info "Checking user: '$username' against pattern: '$user_pattern'" matchlen=$(expr "$username" : "$user_pattern") if test "$matchlen" = "${#username}" then grant "Allowing user: '$username' with pattern: '$user_pattern'" fi done deny "The user is not in the access list for this branch" done ) case "$rc" in grant) send_mail $refname $oldrev $newrev grant >/dev/null "Granting access based on $allowed_users_file" ;; deny) # last chance, if usernameBranches are enabled and # the branch maches the userbranch format allow_userBranches deny >/dev/null "Denying access based on $allowed_users_file" ;; *) ;; esac fi # same as above but for groups allowed_groups_file=$GIT_DIR/info/allowed-groups groups=$(id -G -n) info "The user belongs to the following groups:" info "'$groups'" if test -f "$allowed_groups_file" then rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' | while read heads group_patterns do # does this rule apply to us? head_pattern=${heads#+} matchlen=$(expr "$refname" : "${head_pattern#+}") test "$matchlen" = ${#refname} || continue # if non-ff, $heads must be with the '+' prefix test -n "$noff" && test "$head_pattern" = "$heads" && continue info "Found matching head pattern: '$head_pattern'" for group_pattern in $group_patterns; do for groupname in $groups; do info "Checking group: '$groupname' against pattern: '$group_pattern'" matchlen=$(expr "$groupname" : "$group_pattern") if test "$matchlen" = "${#groupname}" then grant "Allowing group: '$groupname' with pattern: '$group_pattern'" fi done done deny "None of the user's groups are in the access list for this branch" done ) case "$rc" in grant) send_mail $refname $oldrev $newrev grant >/dev/null "Granting access based on $allowed_groups_file" ;; deny) deny >/dev/null "Denying access based on $allowed_groups_file" ;; *) ;; esac fi # default policy , branch not matched by any rule defpol=$(git config acl.defaultPolicy) if [ "$defpol" == "allow" ] then # accept send_mail $refname $oldrev $newrev exit 0 else # last chance, if usernameBranches are enabled and # the branch maches the userbranch format allow_userBranches deny >/dev/null "There are no more rules to check. Denying access" fi # --- Finished