X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=Documentation%2Fhowto%2Fupdate-hook-example.txt;h=3a33696f004493ac55124ece8b3088673b553c3c;hb=767e130915015f897fb87b939843b4882212574b;hp=dacaf17c2efe3044d6f7cc0b0ff616c6cb21a255;hpb=302ebfe52192fff9a2c1c612dff22325fd073acc;p=git.git diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt index dacaf17c..3a33696f 100644 --- a/Documentation/howto/update-hook-example.txt +++ b/Documentation/howto/update-hook-example.txt @@ -1,4 +1,4 @@ -From: Junio C Hamano +From: Junio C Hamano and Carl Baldwin Subject: control access to branches. Date: Thu, 17 Nov 2005 23:55:32 -0800 Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net> @@ -26,63 +26,137 @@ section of the documentation: So if your policy is (1) always require fast-forward push (i.e. never allow "git-push repo +branch:branch"), (2) you have a list of users allowed to update each branch, and (3) you -do not let tags to be overwritten, then: - - #!/bin/sh - # This is a sample hooks/update script, written by JC - # in his e-mail buffer, so naturally it is not tested - # but hopefully would convey the idea. - - umask 002 - case "$1" in - refs/tags/*) - # No overwriting an existing tag - if test -f "$GIT_DIR/$1" - then - exit 1 - fi - ;; - refs/heads/*) - # No rebasing or rewinding - if expr "$2" : '0*$' >/dev/null - then - # creating a new branch - ; - else - # updating -- make sure it is a fast forward - mb=`git-merge-base "$2" "$3"` - case "$mb,$2" in - "$2,$mb") - ;; # fast forward -- happy - *) - exit 1 ;; # unhappy - esac - fi - ;; - *) - # No funny refs allowed - exit 1 - ;; - esac - - # Is the user allowed to update it? - me=`id -u -n` ;# e.g. "junio" - while read head_pattern users - do - if expr "$1" : "$head_pattern" >/dev/null - then - case " $users " in - *" $me "*) - exit 0 ;; # happy - ' * ') - exit 0 ;; # anybody - esac - fi - done - exit 1 - -For the sake of simplicity, I assumed that you keep something -like this in $GIT_DIR/info/allowed-pushers file: +do not let tags to be overwritten, then you can use something +like this as your hooks/update script. + +[jc: editorial note. This is a much improved version by Carl +since I posted the original outline] + +-- >8 -- beginning of script -- >8 -- + +#!/bin/bash + +umask 002 + +# 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. + +verbose=false + +# Default shell globbing messes things up downstream +GLOBIGNORE=* + +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" +} + +# Implement generic branch and tag policies. +# - Tags should not be updated once created. +# - Branches should only be fast-forwarded. +case "$1" in + refs/tags/*) + [ -f "$GIT_DIR/$1" ] && + deny >/dev/null "You can't overwrite an existing tag" + ;; + refs/heads/*) + # No rebasing or rewinding + if expr "$2" : '0*$' >/dev/null; then + info "The branch '$1' is new..." + else + # updating -- make sure it is a fast forward + mb=$(git-merge-base "$2" "$3") + case "$mb,$2" in + "$2,$mb") info "Update is fast-forward" ;; + *) deny >/dev/null "This is not a fast-forward update." ;; + esac + fi + ;; + *) + deny >/dev/null \ + "Branch is not under refs/heads or refs/tags. What are you trying to do?" + ;; +esac + +# 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 [ -f "$allowed_users_file" ]; then + rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' | + while read head_pattern user_patterns; do + matchlen=$(expr "$1" : "$head_pattern") + if [ "$matchlen" == "${#1}" ]; then + 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 [ "$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" + fi + done + ) + case "$rc" in + grant) grant >/dev/null "Granting access based on $allowed_users_file" ;; + deny) deny >/dev/null "Denying access based on $allowed_users_file" ;; + *) ;; + esac +fi + +allowed_groups_file=$GIT_DIR/info/allowed-groups +groups=$(id -G -n) +info "The user belongs to the following groups:" +info "'$groups'" + +if [ -f "$allowed_groups_file" ]; then + rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' | + while read head_pattern group_patterns; do + matchlen=$(expr "$1" : "$head_pattern") + if [ "$matchlen" == "${#1}" ]; then + 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 [ "$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" + fi + done + ) + case "$rc" in + grant) grant >/dev/null "Granting access based on $allowed_groups_file" ;; + deny) deny >/dev/null "Denying access based on $allowed_groups_file" ;; + *) ;; + esac +fi + +deny >/dev/null "There are no more rules to check. Denying access" + +-- >8 -- end of script -- >8 -- + +This uses two files, $GIT_DIR/info/allowed-users and +allowed-groups, to describe which heads can be pushed into by +whom. The format of each file would look like this: refs/heads/master junio refs/heads/cogito$ pasky @@ -91,15 +165,8 @@ like this in $GIT_DIR/info/allowed-pushers file: refs/tags/v[0-9]* junio With this, Linus can push or create "bw/penguin" or "bw/zebra" -or "bw/panda" branches, Pasky can do only "cogito", and I can do -master branch and make versioned tags. And anybody can do -tmp/blah branches. This assumes all the users are in a single -group that can write into $GIT_DIR/ and underneath. - - - - - - - +or "bw/panda" branches, Pasky can do only "cogito", and JC can +do master branch and make versioned tags. And anybody can do +tmp/blah branches. +------------