mirror of
https://github.com/git/git.git
synced 2024-11-18 22:14:34 +01:00
Documentation: shared repository management in tutorial.
The branch policy script I outlined was improved and polished by Carl and posted on the list twice since then. It is a shame not to pick it up, so replace the original outline in howto/update-hook-example.txt with the latest from Carl. Also talk about setting up git-shell to allow git-push/git-fetch only SSH access to a shared repository host in the tutorial. Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
eb0362a467
commit
dc5f9239f7
@ -1,4 +1,4 @@
|
||||
From: Junio C Hamano <junkio@cox.net>
|
||||
From: Junio C Hamano <junkio@cox.net> and Carl Baldwin <cnb@fc.hp.com>
|
||||
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:
|
||||
do not let tags to be overwritten, then you can use something
|
||||
like this as your hooks/update script.
|
||||
|
||||
#!/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.
|
||||
[jc: editorial note. This is a much improved version by Carl
|
||||
since I posted the original outline]
|
||||
|
||||
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
|
||||
-- >8 -- beginning of script -- >8 --
|
||||
|
||||
# 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
|
||||
#!/bin/bash
|
||||
|
||||
For the sake of simplicity, I assumed that you keep something
|
||||
like this in $GIT_DIR/info/allowed-pushers file:
|
||||
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.
|
||||
|
||||
------------
|
||||
|
@ -1636,6 +1636,41 @@ fast forward. You need to pull and merge those other changes
|
||||
back before you push your work when it happens.
|
||||
|
||||
|
||||
Advanced Shared Repository Management
|
||||
-------------------------------------
|
||||
|
||||
Being able to push into a shared repository means being able to
|
||||
write into it. If your developers are coming over the network,
|
||||
this means you, as the repository administrator, need to give
|
||||
each of them an SSH access to the shared repository machine.
|
||||
|
||||
In some cases, though, you may not want to give a normal shell
|
||||
account to them, but want to restrict them to be able to only
|
||||
do `git push` into the repository and nothing else.
|
||||
|
||||
You can achieve this by setting the login shell of your
|
||||
developers on the shared repository host to `git-shell` program.
|
||||
|
||||
[NOTE]
|
||||
Most likely you would also need to list `git-shell` program in
|
||||
`/etc/shells` file.
|
||||
|
||||
This restricts the set of commands that can be run from incoming
|
||||
SSH connection for these users to only `receive-pack` and
|
||||
`upload-pack`, so the only thing they can do are `git fetch` and
|
||||
`git push`.
|
||||
|
||||
You still need to create UNIX user accounts for each developer,
|
||||
and put them in the same group. Make sure that the repository
|
||||
shared among these developers is writable by that group.
|
||||
|
||||
You can implement finer grained branch policies using update
|
||||
hooks. There is a document ("control access to branches") in
|
||||
Documentation/howto by Carl Baldwin and JC outlining how to (1)
|
||||
limit access to branch per user, (2) forbid overwriting existing
|
||||
tags.
|
||||
|
||||
|
||||
Bundling your work together
|
||||
---------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user